Это давняя жалоба на Java, но в основном она бессмысленна и обычно основана на просмотре неверной информации. Обычная фраза выглядит примерно так: "Hello World на Java занимает 10 мегабайт! Зачем это нужно?" Что ж, вот способ заставить Hello World на 64-битной JVM заявить, что он занимает более 4 гигабайт ... по крайней мере, с помощью одной формы измерения.
java -Xms1024m -Xmx4096m com.example.Hello
Различные способы измерения памяти
В Linux команда top дает вам несколько разных чисел для памяти. Вот что он говорит о примере Hello World:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2120 kgregory 20 0 4373m 15m 7152 S 0 0.2 0:00.10 java
- VIRT - это пространство виртуальной памяти: сумма всего на карте виртуальной памяти (см. Ниже). Это в значительной степени бессмысленно, за исключением тех случаев, когда это не так (см. Ниже).
- RES - это размер резидентного набора: количество страниц, которые в настоящее время находятся в ОЗУ. Почти во всех случаях это единственное число, которое следует использовать, когда вы говорите «слишком большой». Но это все равно не очень хорошая цифра, особенно если говорить о Java.
- SHR - это объем резидентной памяти, которая используется совместно с другими процессами. Для процесса Java это обычно ограничивается разделяемыми библиотеками и JAR-файлами с отображением в память. В этом примере у меня был запущен только один процесс Java, поэтому я подозреваю, что 7k является результатом использования библиотек в ОС.
- SWAP не включен по умолчанию и здесь не отображается. Он указывает объем виртуальной памяти, которая в настоящее время находится на диске, действительно ли она находится в пространстве подкачки. ОС очень хорошо хранит активные страницы в ОЗУ, и единственное средство от подкачки - (1) купить больше памяти или (2) уменьшить количество процессов, поэтому лучше не обращать внимания на это число.
Ситуация с диспетчером задач Windows немного сложнее. В Windows XP есть столбцы «Использование памяти» и «Размер виртуальной памяти», но официальная документация ничего не говорит о том, что они означают. Windows Vista и Windows 7 добавляют больше столбцов, и на самом деле они задокументировано. Из них наиболее полезным является измерение «Рабочий набор»; это примерно соответствует сумме RES и SHR в Linux.
Понимание карты виртуальной памяти
Виртуальная память, потребляемая процессом, - это сумма всего, что находится в карте памяти процесса. Сюда входят данные (например, куча Java), а также все общие библиотеки и файлы с отображением памяти, используемые программой. В Linux вы можете использовать команду pmap, чтобы увидеть все, что отображается в процессе. space (здесь и далее я буду ссылаться только на Linux, потому что это то, что я использую; я уверен, что есть эквивалентные инструменты для Windows). Вот отрывок из карты памяти программы «Hello World»; вся карта памяти занимает более 100 строк, и нет ничего необычного в том, чтобы иметь список из тысяч строк.
0000000040000000 36K r-x-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000 8K rwx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000 676K rwx-- [ anon ]
00000006fae00000 21248K rwx-- [ anon ]
00000006fc2c0000 62720K rwx-- [ anon ]
0000000700000000 699072K rwx-- [ anon ]
000000072aab0000 2097152K rwx-- [ anon ]
00000007aaab0000 349504K rwx-- [ anon ]
00000007c0000000 1048576K rwx-- [ anon ]
...
00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000 1024K rwx-- [ anon ]
00007fa1ed2d3000 4K ----- [ anon ]
00007fa1ed2d4000 1024K rwx-- [ anon ]
00007fa1ed3d4000 4K ----- [ anon ]
...
00007fa1f20d3000 164K r-x-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000 28K rwx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000 1576K r-x-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000 16K r-x-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000 4K rwx-- /lib/x86_64-linux-gnu/libc-2.13.so
...
Краткое объяснение формата: каждая строка начинается с адреса виртуальной памяти сегмента. Далее следует размер сегмента, разрешения и источник сегмента. Этот последний элемент является либо файлом, либо "аноном", который указывает блок памяти, выделенный с помощью mmap а>.
Начиная сверху, у нас есть
- Загрузчик JVM (т. Е. Программа, которая запускается при вводе
java
). Это очень мало; все, что он делает, - загружается в разделяемые библиотеки, где хранится реальный код JVM.
- Куча анонимных блоков, содержащих кучу Java и внутренние данные. Это Sun JVM, поэтому куча разбита на несколько поколений, каждое из которых является собственным блоком памяти. Обратите внимание, что JVM выделяет пространство виртуальной памяти на основе значения
-Xmx
; это позволяет иметь непрерывную кучу. Значение -Xms
используется внутри, чтобы указать, какая часть кучи «используется» при запуске программы, и для запуска сборки мусора по мере приближения к этому пределу.
- Отображенный в память JAR-файл, в данном случае файл, содержащий «классы JDK». Когда вы сопоставляете JAR-файл с памятью, вы можете очень эффективно обращаться к файлам в нем (вместо того, чтобы каждый раз читать его с самого начала). Sun JVM будет отображать память для всех JAR в пути к классам; если вашему коду приложения требуется доступ к JAR, вы также можете отобразить его в памяти.
- Данные для двух потоков. Блок 1M - это стек потока. У меня не было хорошего объяснения блока 4k, но @ericsoe идентифицировал его как «защитный блок»: у него нет разрешений на чтение / запись, поэтому при доступе к нему будет происходить ошибка сегмента, и JVM улавливает это и переводит это к
StackOverFlowError
. В реальном приложении вы увидите десятки, если не сотни таких записей, повторяющихся на карте памяти.
- Одна из разделяемых библиотек, в которой хранится фактический код JVM. Их несколько.
- Общая библиотека для стандартной библиотеки C. Это лишь одна из многих вещей, которые загружает JVM и не является строго частью Java.
Общие библиотеки особенно интересны: каждая разделяемая библиотека имеет как минимум два сегмента: сегмент только для чтения, содержащий код библиотеки, и сегмент чтения-записи, который содержит глобальные данные для каждого процесса для библиотеки (я не знаю, что сегмент без разрешений есть; я видел его только на x64 Linux). Часть библиотеки, доступная только для чтения, может использоваться всеми процессами, использующими библиотеку; например, libc
имеет 1,5 МБ виртуальной памяти, которую можно использовать совместно.
Когда важен размер виртуальной памяти?
Карта виртуальной памяти содержит много всего. Некоторые из них доступны только для чтения, некоторые из них являются общими, а некоторые выделяются, но никогда не затрагиваются (например, почти все 4 Гб кучи в этом примере). Но операционная система достаточно умна, чтобы загружать только то, что ей нужно, поэтому размер виртуальной памяти в значительной степени не имеет значения.
Размер виртуальной памяти важен, если вы работаете в 32-разрядной операционной системе, где вы можете выделить только 2 ГБ (или, в некоторых случаях, 3 ГБ) адресного пространства процесса. В этом случае вы имеете дело с ограниченным ресурсом и, возможно, придется пойти на компромисс, например уменьшить размер кучи, чтобы отобразить память большого файла или создать множество потоков.
Но, учитывая, что 64-битные машины распространены повсеместно, я не думаю, что пройдет много времени, прежде чем размер виртуальной памяти станет совершенно несущественной статистикой.
Когда важен размер резидентского набора?
Размер резидентного набора - это та часть пространства виртуальной памяти, которая фактически находится в ОЗУ. Если ваш RSS становится значительной частью вашей общей физической памяти, возможно, пора начать беспокоиться. Если ваш RSS разрастается, занимая всю вашу физическую память, и ваша система начинает менять местами, давно пора начинать беспокоиться.
Но RSS также вводит в заблуждение, особенно на слабо загруженной машине. Операционная система не тратит много усилий на восстановление страниц, используемых процессом. От этого мало что можно получить, и есть вероятность дорогостоящего сбоя страницы, если процесс коснется страницы в будущем. В результате статистика RSS может включать множество страниц, которые не используются активно.
Нижняя линия
Если вы не меняете местами, не беспокойтесь о том, что вам говорят различные статистические данные о памяти. С оговоркой, что постоянно растущий RSS может указывать на некую утечку памяти.
В программе на Java гораздо важнее обращать внимание на то, что происходит в куче. Общий объем занимаемого пространства важен, и вы можете предпринять некоторые шаги, чтобы его уменьшить. Более важным является количество времени, которое вы тратите на сборку мусора, и какие части кучи собираются.
Доступ к диску (т. Е. К базе данных) стоит дорого, а память - дешевая. Если вы можете обменять одно на другое, сделайте это.
person
kdgregory
schedule
18.02.2009