На що споживає пам'ять у процесі Java?


20

ми намагаємось дослідити використання пам'яті Java-процесу при помірному навантаженні.

  PID   USER    PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  12663 test    20   0 8378m 6.0g 4492 S   43  8.4 162:29.95 java

Як ви бачите, у нас є пам'ять резидента на 6Gb. Тепер цікава частина така: процес виконується за допомогою цих парам:

  • -Xmx2048m
  • -Xms2048м
  • -XX: NewSize = 512м
  • -XX: MaxDirectMemorySize = 256м
  • ... деякі інші для GC та ін

Дивлячись на ці налаштування та на фактичне використання пам’яті, ми стикаємось, щоб побачити різницю того, що ми очікуємо, що цей процес використовує і що він насправді використовує.

Зазвичай наші проблеми з пам'яттю вирішуються за допомогою аналізу звалища, але в цьому випадку наша пам’ять використовується десь поза купою.

Запитання: Якими були б кроки, щоб спробувати знайти причину такого високого використання пам'яті? Які інструменти можуть допомогти нам визначити, що використовує пам'ять у цьому процесі?

EDIT 0

Не схоже, що це проблема, пов’язана з купою, оскільки у нас все ще є достатньо місця:

jmap -heap 12663

результати в (відредаговано для економії місця)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 2147483648 (2048.0MB)
NewSize          = 536870912 (512.0MB)
MaxNewSize       = 536870912 (512.0MB)
OldSize          = 1610612736 (1536.0MB)
NewRatio         = 7
SurvivorRatio    = 8
PermSize         = 21757952 (20.75MB)
MaxPermSize      = 85983232 (82.0MB)

New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used

ЗРІД 1

використовуючи pmap, ми можемо побачити, що існує досить деяка кількість асигнувань 64Mb:

pmap -x 12663 | grep rwx | sort -n -k3 | less

призводить до:

... a lot more of these 64Mb chunks
00007f32b8000000       0   65508   65508 rwx--    [ anon ] <- what are these?
00007f32ac000000       0   65512   65512 rwx--    [ anon ]
00007f3268000000       0   65516   65516 rwx--    [ anon ]
00007f3324000000       0   65516   65516 rwx--    [ anon ]
00007f32c0000000       0   65520   65520 rwx--    [ anon ]
00007f3314000000       0   65528   65528 rwx--    [ anon ] 
00000000401cf000       0  241904  240980 rwx--    [ anon ] <- Direct memory ?
000000077ae00000       0 2139688 2139048 rwx--    [ anon ] <- Heap ?

Тож як дізнатися, що це за шматки 64 Мб? Що їх використовує? Які дані є в них?

Спасибі


2
У мене точно така ж проблема ... ось моє запитання. stackoverflow.com/questions/18734389/… У вас є рішення щодо цього?
DeepNightTwo

Відповіді:


21

Проблема може бути пов’язана з цією проблемою glibc .

В основному, коли у вас є декілька потоків, що виділяють пам'ять, glibc збільшить кількість доступних арен, щоб виконати розподіл, щоб уникнути суперечок із блокуванням. Арена розміром 64 Мб. Верхня межа полягає в тому, щоб створити в 8 разів більше числа арен сердечників. Арени створюватимуться на вимогу, коли нитка перейде до арени, яка вже заблокована, щоб вона зростала з часом.

На Java, де ви посипаєте нитки, це може швидко призвести до створення багатьох арен. І розподілення розповсюджуються по всіх цих аренах. Спочатку кожна 64-Мб арена просто відображає невмілену пам'ять, але, виконуючи розподіли, ви починаєте використовувати для них фактичну пам'ять.

Ваш pmap, ймовірно, містить списки, подібні до цього нижче. Зверніть увагу, як 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Тобто вони підсумовують 64Mb.

00007f4394000000 324K rw --- [anon]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560K rw --- [anon]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620K rw --- [anon]
00007f439c09b000 64916K ----- [anon]

Щодо обхідних шляхів : помилка згадує деякі параметри середовища, які ви можете встановити для обмеження кількості арен, але вам потрібна версія glibc досить висока.


5
встановлення Xmx та Xms на однакове значення плюс встановлення змінної середовища "експортувати MALLOC_ARENA_MAX = 4" у скрипті sh, який запускає наш веб-сервіс, допомогло в нашому випадку. До цього ми переживали перезавантаження веб-сервісу через OOM Killer кожні 2 - 8 годин. Версія GLIBC в Ubuntu 14.04 - це 2.19, що добре, оскільки для роботи параметра MALLOC_ARENA_MAX для роботи
MALLOC_ARENA_MAX

Ця відповідь та вищезазначений коментар були для мене рятівницею. У моєму випадку MALLOC_ARENA_MAX = 1 був необхідним та ефективним.
Джон Бачір

3

Як щодо зонда Lamdba ? Крім усього іншого, він може показувати розбиття пам'яті, схожі на знімок екрана нижче:

Вид використання пам'яті зонда Lambda

Іноді pmap -x your_java_pidтакож може бути корисно.


Дякую за вашу відповідь. Якщо я правильно розумію, то зонд Lambda призначений для Apache Tomcat? Що ми не використовуємо ... Щодо pmap я додам інформацію до верхнього повідомлення
Костянтин С.

2

JProfiler може бути те, що ви шукаєте, але це не безкоштовно. Ще одним хорошим та безкоштовним інструментом для дослідження використання пам'яті Java-процесу є Java VisualVM, доступний як інструмент JDK в дистрибутивах Oracle / Sun JDK. Я особисто рекомендую більш цілісний підхід до проблеми (тобто для моніторингу дисків JDK + OS + тощо) - використання деякої системи моніторингу мережі - Nagios, Verax NMS або OpenNMS.


2
Його протікання вихлопне, тому JProfiler тут не дуже допоможе
Асаф Месіка,

2

Проблема знаходиться поза грою, тому найкращими кандидатами є:

JNI leak  
Allocation of direct memory buffer

Через те, що ви обмежили прямий розмір буфера, найкращим кандидатом, на мою думку, є витік JNI.


1

Існує зручний інструмент для перегляду розподілу пам'яті купи, що входить до JDK під назвою jmap, поверх цього у вас також є стек тощо (Xss). Виконайте ці дві команди jmap, щоб отримати більше інформації про використання пам'яті:

jmap -heap <PID>
jmap -permstat <PID>

Щоб отримати ще більше інформації, ви можете підключитися до процесу за допомогою jconsole (також включеного в JDK). Однак Jconsole вимагає конфігурації JMX у додатку.


Дякуємо за вашу відповідь, але проблема, здається, є десь поза грою. Я оновлю верхній пост, щоб відобразити деяку інформацію з jmap
Костянтин С.

Скільки ниток має процес? Розмір стека за замовчуванням становить 2 Мб на більшості платформ, тому помножте їх на кількість потоків. Я був би здивований, якщо на нього припадає вся "відсутність" пам'яті, але, можливо, частина її.
HampusLi

Близько 300 потоків, взято інформацію з pmap, у нас є розмір стека в 1 Мб. І з того ж виводу pmap схоже, що жоден із цих стеків не використовує більше 100 Кб
Костянтин С.

0

Використовуйте JVisualVM. Він має різні різні погляди, які дозволять вам розповісти, скільки пам’яті використовується, PermGen і так далі.

Що стосується відповіді на ваше запитання. Java обробляє пам'ять зовсім інакше, ніж ви могли очікувати.

Коли ви встановлюєте параметри -Xms і -Xmx, ви повідомляєте JVM, скільки пам'яті він повинен виділити в купі, для початку, а також скільки він повинен виділити як максимум.

Якщо у вас є програма java, яка використовувала всього 1м пам'яті, але пройшла в -Xms256m -Xmx2g, то JVM ініціалізує себе з 256 м пам'яті, що використовується. Він не використовуватиме менше цього. Не має значення, що ваша програма використовує лише 1м пам'яті.

По-друге. У вищенаведеному випадку, якщо програма в якийсь момент використовує більше 256м пам'яті, JVM виділить стільки пам'яті, скільки потрібно для обслуговування запиту. Тим НЕ менше, він НЕ БУДЕ падати купи розміру назад до мінімального значення. Принаймні, не за більшості обставин.

У вашому випадку, оскільки ви встановлюєте мінімальний і максимальний об'єм пам'яті на рівні 2 г, JVM виділить 2 г на старті і збереже його.

Управління пам'яттю Java є досить складним, і налаштування використання пам'яті може бути завданням само по собі. Однак є багато ресурсів, які можуть допомогти.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.