У меня проблема с приложением Java, работающим под Linux.
Когда я запускаю приложение, используя максимальный размер кучи по умолчанию (64 МБ), я вижу, используя приложение tops, что 240 МБ виртуальной памяти выделяются для приложения. Это создает некоторые проблемы с некоторыми другими программами на компьютере, которые относительно ограничены в ресурсах.
Насколько я понимаю, зарезервированная виртуальная память не будет использоваться в любом случае, потому что, как только мы достигаем предела кучи, создается OutOfMemoryError
исключение. Я запустил одно и то же приложение под Windows и вижу, что размер виртуальной памяти и размер кучи одинаковы.
Есть ли способ настроить виртуальную память для процесса Java под Linux?
Редактировать 1 : проблема не в куче. Проблема в том, что если я установлю, например, кучу 128 МБ, Linux все равно выделит 210 МБ виртуальной памяти, которая никогда не нужна **.
Редактировать 2 : Использование ulimit -v
позволяет ограничить объем виртуальной памяти. Если размер установлен ниже 204 МБ, приложение не будет работать, даже если ему не нужно 204 МБ, только 64 МБ. Поэтому я хочу понять, почему Java требует так много виртуальной памяти. Можно ли это изменить?
Редактировать 3 : в системе работает несколько других приложений, которые встроены. И система имеет ограничение виртуальной памяти (из комментариев, важные детали).
источник
Ответы:
Это была давняя жалоба на Java, но она в значительной степени бессмысленна и обычно основана на поиске неверной информации. Обычная формулировка выглядит примерно так: «Hello World на Java занимает 10 мегабайт! Зачем это нужно?» Что ж, вот способ заставить Hello World на 64-битной JVM претендовать на 4 гигабайта ... хотя бы одним способом измерения.
Различные способы измерения памяти
В Linux команда top выдает несколько разных чисел для памяти. Вот что говорит пример Hello World:
Ситуация для диспетчера задач Windows немного сложнее. В Windows XP есть столбцы «Использование памяти» и «Размер виртуальной памяти», но в официальной документации ничего не говорится о том, что они означают. Windows Vista и Windows 7 добавляют больше столбцов, и они фактически задокументированы . Из них измерение «Рабочий набор» является наиболее полезным; это примерно соответствует сумме RES и SHR в Linux.
Понимание карты виртуальной памяти
Виртуальная память, используемая процессом, представляет собой сумму всего, что находится в карте памяти процесса. Это включает в себя данные (например, кучу Java), а также все общие библиотеки и файлы отображения памяти, используемые программой. В Linux вы можете использовать команду pmap, чтобы увидеть все объекты, отображенные в пространстве процесса (с этого момента я буду ссылаться только на Linux, потому что это то, что я использую; я уверен, что есть эквивалентные инструменты для Windows). Вот выдержка из карты памяти программы «Hello World»; вся карта памяти имеет длину более 100 строк, и нет ничего необычного в том, чтобы иметь список из тысячи строк.
Краткое объяснение формата: каждая строка начинается с адреса виртуальной памяти сегмента. Далее следуют размер сегмента, разрешения и источник сегмента. Этот последний элемент является либо файлом, либо «anon», который указывает блок памяти, выделенный через mmap .
Начиная сверху, мы имеем
java
). Это очень мало; все, что он делает, это загружает в разделяемые библиотеки, где хранится настоящий код JVM.-Xmx
значения; это позволяет ему иметь непрерывную кучу. Это-Xms
значение используется внутри, чтобы сказать, сколько кучи «используется» при запуске программы, и запустить сборку мусора при приближении к этому пределу.StackOverFlowError
. Для реального приложения вы увидите десятки, если не сотни этих записей, повторенных через карту памяти.Совместно используемые библиотеки особенно интересны: каждая разделяемая библиотека имеет как минимум два сегмента: сегмент только для чтения, содержащий код библиотеки, и сегмент чтения-записи, содержащий глобальные данные о процессах для библиотеки (я не знаю, что сегмент без разрешений есть; я видел его только на x64 Linux). Часть библиотеки, доступная только для чтения, может использоваться всеми процессами, которые используют библиотеку; например,
libc
имеет 1,5 МБ виртуальной памяти, которую можно использовать совместно.Когда важен размер виртуальной памяти?
Карта виртуальной памяти содержит много вещей. Некоторые из них доступны только для чтения, некоторые из них являются общими, а некоторые выделяются, но никогда не затрагиваются (например, почти все 4 Гб кучи в этом примере). Но операционная система достаточно умна, чтобы загружать только то, что ей нужно, поэтому размер виртуальной памяти в значительной степени не имеет значения.
Размер виртуальной памяти важен, если вы работаете в 32-битной операционной системе, где вы можете выделить только 2 ГБ (или, в некоторых случаях, 3 ГБ) адресного пространства процесса. В этом случае вы имеете дело с дефицитным ресурсом, и вам, возможно, придется пойти на компромисс, например, уменьшить размер кучи, чтобы отобразить в памяти большой файл или создать много потоков.
Но, учитывая, что 64-битные машины распространены повсеместно, я не думаю, что пройдет много времени, прежде чем объем виртуальной памяти станет абсолютно неактуальной статистикой.
Когда важен размер резидентного набора?
Размер резидентного набора - это та часть виртуальной памяти, которая фактически находится в ОЗУ. Если ваш RSS становится значительной частью вашей общей физической памяти, возможно, пришло время начать беспокоиться. Если ваш RSS-канал начинает занимать всю вашу физическую память, а ваша система начинает обмениваться, уже давно пора начать беспокоиться.
Но RSS также вводит в заблуждение, особенно на слегка загруженной машине. Операционная система не тратит много сил на восстановление страниц, используемых процессом. Это дает мало пользы и может привести к дорогостоящему отказу страницы, если процесс коснется страницы в будущем. В результате статистика RSS может включать в себя множество страниц, которые не используются активно.
Нижняя граница
Если вы не поменялись местами, не слишком переживайте о том, что говорит вам различная статистика памяти. С оговоркой, что постоянно растущая RSS может указывать на какую-то утечку памяти.
С Java-программой гораздо важнее обратить внимание на то, что происходит в куче. Важное значение имеет общий объем потребляемого пространства, и вы можете предпринять некоторые шаги для его уменьшения. Более важным является количество времени, которое вы тратите на сборку мусора, и какие части кучи собираются.
Доступ к диску (т. Е. К базе данных) стоит дорого, а память - дешево. Если вы можете обменять одно на другое, сделайте это.
источник
Существует известная проблема с Java и glibc> = 2.10 (включает Ubuntu> = 10.04, RHEL> = 6).
Лекарство заключается в том, чтобы установить это env. переменная:
Если вы работаете с Tomcat, вы можете добавить это в
TOMCAT_HOME/bin/setenv.sh
файл.Для Docker, добавьте это в Dockerfile
Есть статья IBM о настройке MALLOC_ARENA_MAX https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en
Это сообщение в блоге говорит
Существует также открытая ошибка JDK JDK-8193521 «glibc тратит память с конфигурацией по умолчанию»
найдите MALLOC_ARENA_MAX в Google или SO для получения дополнительных ссылок.
Вы можете настроить другие параметры malloc для оптимизации фрагментации выделенной памяти:
источник
MALLOC_ARENA_MAX
может замедлить рост памяти, но не решить проблему полностью.Объем памяти, выделенный для процесса Java, в значительной степени соответствует ожидаемому. У меня были похожие проблемы с запуском Java во встроенных системах / системах с ограниченным объемом памяти. Запуск любого приложения с произвольными ограничениями виртуальных машин или в системах, в которых не хватает достаточного количества подкачки, может привести к сбою. Кажется, это характерная черта многих современных приложений, которые не предназначены для использования в системах с ограниченными ресурсами.
У вас есть еще несколько вариантов, которые вы можете попробовать и ограничить объем памяти вашей JVM. Это может уменьшить объем виртуальной памяти:
Кроме того, вы также должны установить для -Xmx (максимальный размер кучи) значение, максимально приближенное к фактическому пиковому использованию памяти вашим приложением. Я считаю, что по умолчанию JVM по-прежнему удваивает размер кучи каждый раз, когда он расширяет его до максимума. Если вы начнете с кучи 32M, а ваше приложение достигнет пика 65M, то в итоге размер кучи увеличится до 32M -> 64M -> 128M.
Вы также можете попробовать это сделать виртуальную машину менее агрессивной в отношении роста кучи:
Кроме того, насколько я помню из экспериментов с этим несколько лет назад, количество загруженных нативных библиотек оказало огромное влияние на минимальную площадь. Загрузка java.net.Socket добавлена более 15M, если я правильно помню (и я, вероятно, не).
источник
Для Sun JVM требуется много памяти для HotSpot, и он отображается в библиотеках времени выполнения в общей памяти.
Если проблема с памятью, рассмотрите возможность использования другой JVM, подходящей для встраивания. У IBM есть j9, и есть open source "jamvm", который использует библиотеки путей к классам GNU. Также у Sun есть Squeak JVM, работающая на SunSPOTS, так что есть альтернативы.
источник
Просто подумал, но вы можете проверить влияние на
ulimit -v
опции .Это нереальное решение, поскольку оно ограничит адресное пространство, доступное для всех процессов, но это позволит вам проверить поведение вашего приложения с ограниченной виртуальной памятью.
источник
Одним из способов уменьшения кучи системы с ограниченными ресурсами может быть использование переменной -XX: MaxHeapFreeRatio. Обычно это значение равно 70, и это максимальный процент кучи, который свободен до того, как GC сжимает его. Если установить более низкое значение, вы увидите, например, в профилировщике jvisualvm, что для вашей программы обычно используется меньший размер кучи.
РЕДАКТИРОВАТЬ: Чтобы установить небольшие значения для -XX: MaxHeapFreeRatio, вы также должны установить -XX: MinHeapFreeRatio Например
EDIT2: добавлен пример для реального приложения, которое запускается и выполняет ту же задачу, одно с параметрами по умолчанию, а другое с 10 и 25 в качестве параметров. Я не заметил какой-либо реальной разницы в скорости, хотя в теории Java должен использовать больше времени для увеличения кучи в последнем примере.
В итоге максимальная куча составляет 905, использованная куча - 378
В итоге максимальная куча составляет 722, использованная куча - 378
Это на самом деле имеет некоторый недостаток, так как наше приложение работает на сервере удаленного рабочего стола, и многие пользователи могут запускать его одновременно.
источник
Sun java 1.4 имеет следующие аргументы для управления объемом памяти:
http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html
Java 5 и 6 есть еще. См. Http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
источник
Нет, вы не можете настроить объем памяти, необходимый для виртуальной машины. Тем не менее, обратите внимание, что это виртуальная память, а не резидентная, поэтому она остается без вреда, если не используется на самом деле.
С другой стороны, вы можете попробовать другую JVM, чем Sun, с меньшим объемом памяти, но я не могу советовать здесь.
источник