Почему размер кучи фиксирован на JVM?


20

Может кто-нибудь объяснить мне, почему JVM (я не проверял слишком много, но я никогда не видел такой, которая не делала так) должны работать с фиксированным размером кучи? Я знаю, что проще реализовать в простой непрерывной куче, но JVM Sun уже более десяти лет, поэтому я ожидаю, что у них было время, чтобы улучшить это.

Необходимость определить максимальный объем памяти вашей программы во время запуска кажется делом 1960-х годов, а затем возникают плохие взаимодействия с управлением виртуальной памятью ОС (сбор данных извлекается из памяти, невозможность определить, сколько памяти занимает процесс Java). действительно используя со стороны ОС, огромное количество пространства ВМ тратится впустую (я знаю, вам все равно на ваших модных 48-битных машинах ...)). Я также предполагаю, что различные печальные попытки создания небольших операционных систем внутри JVM (серверы приложений EE, OSGi), по крайней мере, частично виноваты в этом обстоятельстве, потому что запуск нескольких процессов Java в системе неизменно приводит к потере ресурсов, потому что вы должны дать каждому из них память, которую он может использовать в пике.

Удивительно, но Google не вызвал бурь возмущения по поводу этого, что я ожидаю, но они могли быть просто похоронены под миллионами людей, узнавших о фиксированном размере кучи и просто приняв это за факт.


Проектирование серверов приложений EE имело бы смысл даже без этих «обстоятельств», поскольку самой JVM требуется некоторое пространство, а переключение между потоками обходится дешевле, чем переключение между процессами - это одна из вещей, которая сделала Java большой в конце 90-х годов.
Майкл Боргвардт

Это что-то вроде ругательства, и мне интересно, сколько мыслей ты вложил в это. Например, как изменилось бы взаимодействие GC / swap, если бы у вас не было предела кучи? Как пространство VM потрачено впустую? Вы получаете ваше пространство 2 / 3Gb независимо от того, используете ли вы его или нет, и если вы расширяете границы этого пространства, не имеет значения, есть ли у вас фиксированная или плавающая куча. В связи с этим, как несколько JVM тратят впустую что-либо кроме свопа (который должен быть настроен соответственно для цели машины)?
kdgregory

2
Это своего рода напыщенная речь, но она основана на многолетнем опыте написания и эксплуатации платформы на основе Java. Если вы не выполняете своп (потому что это сделает вашу систему не отвечающей на 20 минут, пока у разгружающегося процесса не останется свободного места для выделения) и отключите перегрузку памяти по соображениям стабильности (убийца OOM не очень хорош в выборе жертв ), вам небезразлично пространство VM, и вопреки тому, что подразумевают люди, представленные ниже, запуск Java VM с -Xmx2048m немедленно выделит 2 ГБ виртуальной памяти (по крайней мере, в моей Sun JVM в Linux) для программы с одной переменной.
themel

Отличный вопрос. Интересно то же самое. Но какие из «фактов», представленных здесь в q, и ответы верны?
Мартин Ба,

Для реакции, которую вы искали, просто отправляйтесь на поисковик солнечных лучей ... например, здесь , здесь и здесь . Прочитайте их и почувствуйте возмущенный гнев :)
Basic

Ответы:


23

Вы неправы. Размер кучи JVM не фиксирован, а ограничен:

  • -Xmx устанавливает максимальный объем памяти кучи
  • -Xms устанавливает минимальный объем памяти кучи

Установка верхнего предела необходима по нескольким причинам. Во-первых, он сообщает сборщику мусора, когда начинать действовать. Во-вторых, это предотвращает засорение JVM всей машины, потребляя слишком много памяти. Минимальный размер кучи, вероятно, полезен для резервирования того объема памяти, который необходим программе, по крайней мере, для предотвращения нехватки памяти (поскольку другие процессы потребляют слишком много).


9
нет минимальное значение по умолчанию , чтобы избежать медленного запуска , когда много нужно выделить в результате неоднократных увеличения кучи, подкачки будет иметь дело с физической оперативной памяти на исходе
трещотки урод

@ratchetfreak это была моя вторая догадка ;-)
user281377

@ user281377 Если это так, то как C # может нормально работать без максимального объема памяти кучи?
cmorse

cmorse: я могу только догадываться. Возможно, Java нацелена на большие серверы, где многие приложения совместно используют ресурсы, и желательны строго установленные ограничения, в то время как .net скорее предназначен для ПК и небольших, более выделенных серверов.
user281377

@ user281377: Приложения Java, которые я использовал, которые исчерпали пространство кучи, как правило, очень плохо с этим справляются, обычно просто сбой или очень ненадежный после этого. А ASP.net отлично работает как на больших, так и на маленьких серверах. Что я действительно не понимаю, так это то, почему по умолчанию Java устанавливает этот предел. Я хотел бы услышать о причинах их решения ... Я уверен, что у них была веская причина.
смор

6

Я полагаю, что ответ имеет отношение к наследию Java. Первоначально он был разработан как язык, который будет использоваться для встроенных систем, где очевидно, что ресурсы ограничены, и вы не хотите, чтобы процессы просто сожрали все, что доступно. Это также помогает в системном администрировании, поскольку упрощает выделение ресурсов на сервере, если вы можете установить ограничения ресурсов. Я вижу, что последние версии JVM, похоже, используют несколько не непрерывных куч, хотя, конечно, все это выглядит как одна куча для вашего кода.

(FWIW, вы должны были указать требования к памяти для программы в версиях Apple, предшествовавших Darwin MacOS [во всяком случае, вплоть до System 7, которая была последней, которую я использовал), что было в 80-е годы.)


1
+1 - за то, что (1) единственный ответ, который фактически ответил на вопрос, и (2) правдоподобность.
kdgregory

2

Вам нужно дать GC какой-то механизм, чтобы сообщить ему, когда запускать, иначе ваша программа заполнит все пространство виртуальной памяти. Есть много альтернативных способов запуска GC: истекшее время, количество распределений, количество назначений, возможно, другие, о которых я не могу думать прямо сейчас. IMO ни один из них не так хорош, как просто установка границы памяти и запуск GC, когда выделенное пространство достигает этой границы.

Ключ должен установить правильные границы. Я смотрю -msкак «это то, сколько памяти требуется моему приложению» и -mxкак «оно никогда не должно превышать это количество». В производственном развертывании оба должны быть близкими, если не равными, и они должны основываться на фактических, измеренных требованиях.

Ваши опасения по поводу «потраченной впустую» виртуальной памяти неуместны: она виртуальная, она (почти) бесплатная. Да, выделение кучи слишком большого размера означает, что вы не можете запустить столько потоков или загрузить столько файлов с отображенной памятью. Но это часть дизайна приложения: у вас ограниченные ресурсы, вам нужно разделить их так, чтобы ваше приложение могло работать. В куче "C-style", которая будет расширяться до тех пор, пока вы не достигнете вершины памяти, основная проблема остается той же, вам просто не нужно думать об этом, пока у вас не возникнут проблемы.

Единственное, что большая куча может «расточить» - это пространство подкачки, потому что все записываемые сегменты требуют обязательной подкачки. Но это является частью конструкции системы: если вы хотите, чтобы на одной машине было запущено много JVM, увеличьте объем подкачки или уменьшите выделение кучи. Если они начинают биться, вы пытаетесь сделать слишком много с системой; купите больше памяти (и если вы все еще используете 32-битный процессор, купите новую коробку).


1
Но порог для запуска GC не должен иметь ничего общего с фиксированным порогом, который общее использование памяти программой не может превышать. (На самом деле это, вероятно, не должно быть; если у вас большая куча, и вы можете использовать только GC, когда она заполнена, у вас обязательно будут периоды GC, но очень редкие). Смотрите сборку мусора поколений.
Бен

@ Бен - да, ты прав. И мое второе предложение указывает на то, что есть альтернативы. Однако я не согласен с тем, что куча фиксированного размера - это неправильный способ управления GC в общем случае. Правильно настроенная JVM использует «правильный» размер кучи; по моему опыту, длинные GC-паузы случаются, когда JVM не был должным образом настроен. И часто «правильный» размер кучи намного меньше, чем вы думаете.
kdgregory

-2

Как говорит user281377, вы указываете только верхний предел того, сколько памяти может занимать ваш процесс . Конечно, само приложение будет занимать только необходимое ему пространство.

Должен ли существовать верхний предел по умолчанию или нет - это другой вопрос, как за, так и против.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.