Почему (Sun) JVM имеет фиксированный верхний предел использования памяти (-Xmx)? - PullRequest
44 голосов
/ 29 июля 2010

В духе вопроса Java: почему существует MaxPermSize? , я хотел бы спросить, почему Sun JVM использует фиксированный верхний предел для размера своего пула выделения памяти.

По умолчанию используется 1/4 вашей физической памяти (с верхним и нижним пределом);как следствие, если у вас есть приложение, требующее памяти, вы должны вручную изменить предел (параметр -Xmx), или ваше приложение будет работать плохо, возможно даже падение с OutOfMemoryError.

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

Это решит целый ряд общих проблем с программным обеспечением Java (просто посмотрите в Google, сколько подсказок в сети).для решения проблем путем установки -Xmx).

Редактировать:

В некоторых ответах указывается, что это защитит остальную часть системы от Java-программы с запускомутечка памяти;без ограничений это приведет к разрушению всей системы, исчерпав всю память.Это верно, однако, это в равной степени верно и для любой другой программы, и современные операционные системы уже позволяют ограничивать максимальный объем памяти для программы (Linux ulimit, Windows "Job Objects").Так что это на самом деле не отвечает на вопрос: «Почему JVM делает это иначе, чем большинство других программ / сред выполнения?».

Ответы [ 8 ]

25 голосов
/ 29 июля 2010

Почему этот фиксированный лимит существует?Почему JVM не выделяет память по мере необходимости, как это делают нативные программы в большинстве операционных систем?

Причина в том, что НЕ , что GC должен знать заранее, какова максимальная кучаРазмер может быть.JVM явно способна расширять свою кучу ... до максимума ... и я уверен, что это будет относительно небольшое изменение, чтобы удалить этот максимум.(В конце концов, другие реализации Java делают это.) И в равной степени можно было бы иметь простой способ сказать «используйте столько памяти, сколько вам нравится» в JVM.

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

В основном, многие операционные системы (например, Windows, Linux) страдают от серьезного снижения производительности, если какое-либо приложение пытается использовать всю доступную память.Например, в Linux система может плохо работать, в результате чего все в системе работает невероятно медленно.В худшем случае система не сможет запустить новые процессы, и существующие процессы могут начать сбой, когда операционная система отклонит их (законные) запросы на выделение дополнительной памяти.Часто единственным вариантом является перезагрузка.

Если JVM по умолчанию работала с неограниченной кучей, каждый раз, когда кто-то запускал программу Java с утечкой памяти ... или просто пытался использовать слишком много памяти... они рискуют сломать всю операционную систему.

Таким образом, наличие привязки к куче по умолчанию - хорошая вещь, потому что:

  • защищает работоспособность вашей системы,
  • поощряет разработчиков / пользователей думают об использовании памяти «голодными» приложениями, а
  • it потенциально позволяет оптимизировать сборщик мусора.(Как подсказывают другие ответы: это правдоподобно, но я не могу это подтвердить.)

РЕДАКТИРОВАТЬ

В ответ на комментарии:

  • Это на самом деле не имеет значения , почему виртуальные машины Sun живут в ограниченной куче, а другие приложения - нет.Они делают, и преимущества этого (ИМО) очевидны.Возможно, более интересный вопрос заключается в том, почему другие управляемые языки не по умолчанию не устанавливают ограничения для своих куч.

  • Подходы -Xmx и ulimitкачественно другой.В первом случае JVM полностью знает ограничения, в которых она работает, и получает возможность соответствующим образом управлять использованием памяти.В последнем случае первое, что типичное C-приложение знает об этом, это когда происходит сбой вызова malloc.Типичный ответ - выйти с кодом ошибки (если программа проверяет результат malloc) или умереть с ошибкой сегментации.ОК, приложение C теоретически может отслеживать объем используемой памяти и пытаться реагировать на надвигающийся кризис памяти.Но это будет тяжелая работа.

  • Другая особенность приложений Java и C / C ++ состоит в том, что первые имеют тенденцию быть и более сложными, и более продолжительными.На практике это означает, что Java-приложения чаще страдают от медленных утечек.В случае C / C ++ тот факт, что управление памятью сложнее, означает, что разработчики не пытаются создавать отдельные приложения такой сложности.Скорее, они с большей вероятностью построят (скажем) сложную службу, имея в своем составе ветвь дочерних процессов для выполнения каких-либо действий ... и затем завершая работу.Это, естественно, смягчает эффект утечек памяти в дочернем процессе.

  • Интересна идея JVM, отвечающей "адаптивно" на запросы от ОС, чтобы вернуть память.Но есть БОЛЬШАЯ проблема.Чтобы вернуть сегмент памяти, JVM сначала должна очистить все доступные объекты в сегменте.Обычно это означает запуск сборщика мусора.Но запуск сборщика мусора - это последняя вещь, которую вы хотите сделать, если система находится в кризисе памяти ... потому что в значительной степени гарантированно генерируется пейджинг виртуальной памяти.

6 голосов
/ 30 июля 2010

Хм, я пока попробую обобщить ответы.

Нет технической причины, по которой JVM должна иметь жесткое ограничение для размера кучи.Он мог бы быть реализован без одного, и на самом деле многие другие динамические языки не имеют этого.

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

Sun могла бы опустить эту функциюи просто сказал людям использовать собственные механизмы ограничения ресурсов ОС, но они, вероятно, хотели всегда иметь ограничение, поэтому они реализовали его сами.В любом случае, JVM должна знать о любом таком ограничении (чтобы адаптировать свою стратегию GC), поэтому использование встроенного в ОС механизма не сэкономило бы много усилий при программировании.

Кроме того, есть одна причина, почемутакой встроенный лимит более важен для JVM, чем для «нормальной» программы без GC (такой как программа на C / C ++):

В отличие от программы с ручным управлением памятью, программа, использующая GC, делаетна самом деле не имеют четко определенных требований к памяти, даже с фиксированными входными данными.Он имеет только минимальное требование, то есть сумму размеров всех объектов, которые фактически живут (достижимы) в данный момент времени.Однако на практике программе потребуется дополнительная память для удержания мертвых, но еще не объектов GCed, поскольку GC не может сразу собрать все объекты, так как это может привести к чрезмерным накладным расходам GC.Таким образом, GC только включается время от времени, и поэтому требуется некоторая «передышка» в куче, где мертвые объекты могут ожидать GC.

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

3 голосов
/ 29 июля 2010

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

Это связано с тем, что с точки зрения приложения более эффективно использовать больше ресурсов, чем прилагать усилия для использования ресурсов, которые у вас уже есть, для полного использования. Это имеет смысл для многих (если не для большинства) применений Java, которые являются настройкой сервера, где приложение буквально является единственным, что имеет значение на сервере. Как правило, он немного менее идеален, когда вы пытаетесь реализовать клиент на Java, который будет одновременно работать с десятками других приложений.

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

2 голосов
/ 29 июля 2010

Это связано с конструкцией JVM. Другие JVM (например, от Microsoft и некоторых IBM) могут использовать всю память, доступную в системе, при необходимости, без произвольного ограничения.

Я считаю, что это позволяет GC-оптимизации.

1 голос
/ 30 июля 2010

Я думаю, что верхний предел для памяти связан с тем фактом, что JVM является виртуальной машиной.

Поскольку любая физическая машина имеет заданный (фиксированный) объем ОЗУ, значит, виртуальная машина имеет его.*

Максимальный размер облегчает управление JVM в операционной системе и обеспечивает некоторый прирост производительности (меньше подкачки).

Sun 'JVM также работает в довольно ограниченной аппаратной архитектуре (встроенные системы ARM), и там управление ресурсами имеет решающее значение.

0 голосов
/ 05 марта 2011

Один ответ, который никто не дал выше, заключается в том, что JVM использует пулы кучи и без кучи памяти. Установка верхнего предела для кучи определяет не только объем доступной памяти для пулов кучи памяти, но также определяет объем памяти, доступный для использования без HEAP. Я полагаю, что JVM могла бы просто разместить не-кучу вверху виртуальной памяти и кучу внизу виртуальной памяти и расти как навстречу друг другу.

Память без кучи включает библиотеки DLL или SO, которые включают в себя JVM и любой используемый код, а также скомпилированный код Java, стеки потоков, собственные объекты, PermGen (метаданные о скомпилированных классах) и другие виды использования. Я видел сбой Java-программ, потому что куче памяти было выделено так много, что приложению не хватило памяти. Именно здесь я узнал, что может быть важно зарезервировать память для использования без кучи, не устанавливая кучу слишком большой.

Это имеет гораздо большее значение в 32-разрядном мире, где приложение часто имеет только 2 ГБ виртуального адресного пространства, чем, конечно, в 64-разрядном мире.

0 голосов
/ 29 июля 2010

Не имеет ли больше смысла разделять верхнюю границу, запускающую GC, и максимальную, которая может быть выделена? Как только выделенная память достигает верхней границы, GC может включить и освободить часть памяти для свободного пула. вроде как я убираю свой стол, которым делюсь со своим коллегой. У меня большой стол, и мой порог того, сколько мусора я могу терпеть на столе, намного меньше размера моего стола. Мне не нужно заполнять каждый доступный дюйм, прежде чем я собираю мусор.

Я мог бы также вернуть часть рабочего места, которое я использую, моему коллеге, который разделяет мой рабочий стол .... Я понимаю, что jvms не возвращает память обратно в систему после того, как они выделили ее себе , но так быть не должно, нет?

0 голосов
/ 29 июля 2010

Распределяет память по мере необходимости, до -Xmx;)

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

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

Нативные программы могут умереть из-за ошибок нехватки памяти, так как нативные приложения также имеют ограничение памяти: память, доступная в системе - память, уже находящаяся в других приложениях.

JVM также необходим непрерывный блок системной памяти для эффективного выполнения сборки мусора.

EDIT

Непрерывное требование памяти или здесь

JVM, по-видимому, освободит часть памяти, но это редко со стандартной конфигурацией.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...