Ответ 1
Это давняя жалоба на 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, чтобы увидеть все вещи, отображаемые в пространстве процесса (отсюда я только собираюсь обратиться к 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, вы также можете его отобразить на карте.
- Данные по потоку для двух потоков. Блок 1М представляет собой стек потоков; Я не знаю, что входит в блок 4K. Для реального приложения вы увидите десятки, если не сотни этих записей повторяются через карту памяти.
- Одна из разделяемых библиотек, которая содержит фактический код JVM. Есть несколько из них.
- Общая библиотека для стандартной библиотеки C. Это всего лишь одна из многих вещей, которые загружают JVM, которые не являются строго частью Java.
Особый интерес представляют разделяемые библиотеки: каждая разделяемая библиотека имеет как минимум два сегмента: сегмент, предназначенный только для чтения, содержащий код библиотеки, и сегмент чтения-записи, который содержит глобальные данные для каждого процесса для библиотеки (я не знаете, что такое сегмент без разрешений, я видел его только на x64 Linux). Доступная только для чтения часть библиотеки может быть разделена между всеми процессами, использующими библиотеку; например, libc
имеет 1,5 М виртуального пространства памяти, которое может быть общим.
Важно знать размер виртуальной памяти?
Карта виртуальной памяти содержит много материала. Некоторые из них доступны только для чтения, некоторые из них являются общими, а некоторые из них выделяются, но не затрагиваются (например, почти весь 4Gb кучи в этом примере). Но операционная система достаточно умна, чтобы загружать только то, что ей нужно, поэтому размер виртуальной памяти в значительной степени не имеет значения.
Если размер виртуальной памяти важен, это если вы работаете в 32-разрядной операционной системе, где вы можете выделять только 2 Гбит (или, в некоторых случаях, 3Gb) адресного пространства процесса. В этом случае вы имеете дело с ограниченным ресурсом и, возможно, придется делать компромиссы, например, уменьшать размер вашей кучи, чтобы карта памяти отображала большой файл или создавала много потоков.
Но, учитывая, что 64-разрядные машины повсеместны, я не думаю, что это будет задолго до того, как размер виртуальной памяти будет абсолютно несущественной статистикой.
Когда задан размер резидентного регистра?
Размер резидентного набора - это часть виртуального пространства памяти, которое фактически находится в ОЗУ. Если ваш RSS станет значительной частью вашей общей физической памяти, возможно, пора начать беспокоиться. Если ваш RSS растет, чтобы охватить всю вашу физическую память, и ваша система начнет меняться, это хорошо прошлое, чтобы начать беспокоиться.
Но RSS также вводит в заблуждение, особенно на слегка загруженной машине. Операционная система не требует больших усилий для восстановления страниц, используемых процессом. При этом мало пользы от этого, а также вероятность дорогостоящей ошибки страницы, если процесс коснется страницы в будущем. В результате статистика RSS может включать в себя множество страниц, которые не активно используются.
Нижняя линия
Если вы не меняете местами, не беспокойтесь о том, что вам говорят разные статистические данные. С предупреждением о том, что постоянно растущий RSS может указывать на какую-то утечку памяти.
С помощью программы Java гораздо важнее обратить внимание на то, что происходит в куче. Общее количество потребляемого пространства важно, и есть несколько шагов, которые вы можете предпринять, чтобы уменьшить это. Более важным является количество времени, которое вы тратите на сбор мусора, и какие части кучи собираются.
Доступ к диску (то есть к базе данных) стоит дорого, а память дешевая. Если вы можете обменять один на другой, сделайте это.