четверг, 15 июня 2017 г.

JAVA memory usage. Часть 1.

В этом посте будет рассмотриваться процесс JVM и в общих чертах описан механизм использования им памяти.

В ОС Linux процессы не работают с памятью напрямую. Каждому процессу выделяется виртуальное пространство, к которому он обращается при необходимости. В момент непосредственного использования памяти ядро системы ассоциирует адрес в виртуальном адресном пространстве процесса со страницами памяти, которыми располагает система (физически существующей памятью). Процесс обеспечивается механизмом lazy allocation.

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

Из принципа работы этого механизма: процессу/процессам ОС может быть выделено больше "виртуальной" памяти, чем физически располагает система. Подобная ситуация не всегда допустима, поэтому процессу, запрашивающему себе память в своём виртуальном адресном пространстве иногда требуется явно указать, как она будет соотносится с физической памятью системы. В связи с этим для дальнейшего понимания вывода диагностических утилит, необходимо ввести классификацию памяти.

Reserved memory - память, которая может быть будет выделена процессу. У любого процесса Reserved memory может быть больше сумы физического объёма памяти в системе.
Выделяется в виртуальном адресном пространстве без выделения физической памяти. Выделяется методом mmap  с указанием ключа PROT_NONE. Т.к. страницы выделены в витр. адресном пространстве, они резервируют часть адресов этого адресного пространства и другие вызовы mmap не будут пытаться использовать уже занятые адреса, но обращния к этим страницам будет вызывать ошибку с отправкой процессу сигнала SIGSEGV.

Commited memory - память в виртуальном адресном пространстве, выделенная процессу. От reserved отличается тем, что резервируется не с флагом PROT_NONE, а, например, с PROT_READ | PROT_WRITE. Страницы commited memory, в отличие от reserved memory, не только зарезервированы в виртуальном адресном пространстве процесса, но и могут быть замаплены на физическую память системы без получения процессом SIGSEGV. При обращении к таким страницам ядро обрабатывает этот вызов, привязывая страницы памяти виртуального адресного пространства процесса и физически существующей памяти прозрачно для процесса.

Resident memory - память, выделенная процессу, в которую процесс записал что-либо.

Как это соотносится с памятью (heap) процесса JVM

Resident memory

Для примера можно рассмотреть процесс
java -jar /opt/gcviewer/gcviewer-1.35-SNAPSHOT.jar
вывод top по этому процессу
  PID USER      PR  NI    VIRT    RES       SHR S  %CPU %MEM  TIME+ COMMAND                  
 7284 user          20   0  5299360 210658  29432 S      0.3       1.6         0:03.74      java   

Столбец RES означает, сколько памяти в данный момент используется процессом. В моём случае используется 210658 килобат. Это resident memory или по-другому RSS (Resident Set Size).
На графике использования heap из jvisualvm синяя область - это часть resident memory процесса. То есть та память, которая заполнена чем-либо.

Использующаяся часть heap - это, конечно, не вся resident memory рассматриваемого процесса. Вся resident memory процесса будет складываться из занятых  Heap и Native областей.

Commited memory.

На том же графике можно увидеть, что текущий Heap size состовляет около 150 Мб.
Текуций heap size JVM - это commited memory.  
Текущий размер heap  - это не вся commited memory рассматриваемого процесса, а её часть. 
Общий объём commited memory процесса складывается из commited memory heap и native областей памяти процесса.


Reserved memory

Если запустить тот же процесс, задав размеры heap ключами -Xms512m -Xmx100G, что заведомо больше, чем есть на моём сервере,
java -Xms512m -Xmx100G, -jar /opt/gcviewer/gcviewer-1.35-SNAPSHOT.jar
вывод top покажет, что занимаемая процессом Resident memory практически не изменилась и составлет 219560 Кбайт
 Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2.7 us,  1.1 sy,  0.0 ni, 94.6 id,  0.9 wa,  0.4 hi,  0.3 si,  0.0 st
KiB Mem :  8075464 total,  1616492 free,  3249608 used,  3209364 buff/cache
KiB Swap:  3145724 total,  3145724 free,        0 used.  4035756 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                   
 8398 user      20   0  0.103t 219560  29464 S   0.3  2.7   0:03.79 java                          
На графике jvisualvm
Размер использующейся в Heap памяти (Resident memory) остался почти таким же, как и при предыдущем запуске - около 20 Mb.

Размер выделенной под heap Commited memory сразу после запуска составляет 512Mб. С точки зрения JVM ключ -Xms указывает, какой размер Heap должен быть выделен при старте JVM. С точки зрения OC, виртуальное адресноге пространство памяти процесса JVM, равное 512m, должно быть зарезервировано в памяти ОС.

 JVM смогла запуститься без ошибок, несмотря на то, что заданная верхняя граница памяти Heap превышает количество памяти на сервере. То есть в виртуальном адресном пространстве памяти процесса было зарезервировано 100Gb, но пока эта память не используется, она никак не привязяна к физической памяти. -Xmx100G - это Reserved memory. Когда Heap (в моём примере он равен 512M) заполнится до такой степени, что JVM потребуется его увеличение, JVM запросит у ОС зарезервированную память (reserved memory).



В моём примере во время старта JVM под процесс выделяется 512mb (commited memory) и резервируется рамять в 100Gb (reserved memory). После увеличения размера heap (не графике справа) размер commited memory стал около 600 mb

Комментариев нет:

Отправить комментарий