Co zużywa pamięć w procesie Java?

20

staramy się zbadać wykorzystanie pamięci przez proces Java przy umiarkowanym obciążeniu.

  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

Jak widać mamy pamięć rezydentną na poziomie 6 Gb. Ciekawe jest to, że proces jest wykonywany przy użyciu następujących parametrów:

  • -Xmx2048m
  • -Xms2048m
  • -XX: NewSize = 512 m
  • -XX: MaxDirectMemorySize = 256m
  • ... kilka innych do GC i tak dalej

Przyglądając się tym ustawieniom i faktycznemu wykorzystaniu pamięci, z trudem dostrzegamy różnicę między tym, czego oczekujemy od tego procesu a tym, czego on faktycznie używa.

Zwykle nasze problemy z pamięcią są rozwiązywane przez analizę zrzutu sterty, ale w tym przypadku nasza pamięć jest używana gdzieś poza stertą.

Pytania: Jakie byłyby kroki, aby spróbować znaleźć przyczynę tak wysokiego zużycia pamięci? Jakie narzędzia mogą pomóc nam zidentyfikować, co wykorzystuje pamięć w tym procesie?

EDYCJA 0

Nie wygląda na to, że jest to problem związany ze stertą, ponieważ wciąż mamy tam dość miejsca:

jmap -heap 12663

wyniki w (edytowane w celu zaoszczędzenia miejsca)

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

EDYCJA 1

za pomocą pmap możemy zobaczyć, że istnieje całkiem sporo alokacji 64Mb:

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

prowadzi do:

... 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 ?

Jak więc dowiedzieć się, jakie są te fragmenty 64Mb? Do czego ich używa? Jakie są w nich dane?

Dzięki

Konstantin S.
źródło
2
Mam dokładnie ten sam problem ... oto moje pytanie. stackoverflow.com/questions/18734389/... Czy masz na to rozwiązanie?
DeepNightTwo

Odpowiedzi:

21

Problem może być związany z tym problemem glibc .

Zasadniczo, gdy masz wiele wątków alokujących pamięć, glibc przeskaluje liczbę dostępnych aren do przydzielenia, aby uniknąć rywalizacji o blokadę. Arena ma 64 MB wielkości. Górny limit to utworzenie 8-krotnej liczby aren rdzeni. Areny będą tworzone na żądanie, gdy wątek uzyska dostęp do areny, która jest już zablokowana, dzięki czemu rośnie z czasem.

W Javie, gdzie posypujesz się wątkami, może to szybko doprowadzić do utworzenia wielu aren. I przydziały są rozrzucone po tych wszystkich arenach. Początkowo każda arena 64 Mb jest po prostu mapowana nieprzydzieloną pamięcią, ale w miarę przydzielania alokacji zaczynasz wykorzystywać dla nich rzeczywistą pamięć.

Twoja pmap prawdopodobnie zawiera listę podobną do tej poniżej. Zwróć uwagę, jak 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Oznacza to, że sumują się do 64 Mb.

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

Jeśli chodzi o obejścia : błąd wspomina o niektórych parametrach środowiska, które można ustawić w celu ograniczenia liczby aren, ale potrzebujesz wystarczająco wysokiej wersji glibc.

chrześcijanin
źródło
5
ustawienie Xmx i Xms na tę samą wartość, a także ustawienie zmiennej środowiskowej „export MALLOC_ARENA_MAX = 4” w skrypcie sh, który uruchamia nasz serwis internetowy, pomógł w naszym przypadku. Wcześniej usługa sieciowa uruchamiała się ponownie z powodu OOM Killer co 2 do 8 godzin. Wersja GLIBC w Ubuntu 14.04 to 2.19, co jest dobre, ponieważ musi być> = 2.16, aby ustawienie MALLOC_ARENA_MAX działało
Kluyg
Ta odpowiedź i powyższy komentarz były dla mnie ratunkiem. W moim przypadku MALLOC_ARENA_MAX = 1 był niezbędny i skuteczny.
John Bachir,
3

Co powiesz na sondę Lamdba ? Między innymi może pokazać awarie użycia pamięci podobne do zrzutów ekranu poniżej:

Widok użycia pamięci sondy lambda

Czasami pmap -x your_java_pidmoże być również pomocna.

Janne Pikkarainen
źródło
Dziękuję za odpowiedź. Jeśli dobrze rozumiem, to Sonda Lambda jest dla Apache Tomcat? Z których nie korzystamy ... Jeśli chodzi o pmap, dodam informacje do najwyższego postu
Konstantin S.
2

JProfiler może być czymś, czego szukasz, ale nie jest bezpłatny. Innym dobrym i bezpłatnym narzędziem do badania wykorzystania pamięci przez proces Java jest Java VisualVM dostępny jako narzędzie JDK w dystrybucjach Oracle / Sun JDK. Osobiście zaleciłbym bardziej całościowe podejście do problemu (tj. Monitorowanie dysków JDK + OS + itp.) - użycie jakiegoś systemu monitorowania sieci - Nagios, Verax NMS lub OpenNMS.

Xantross
źródło
2
Jego wyciek jest nieudany, więc JProfiler tak naprawdę tu nie pomoże
Asaf Mesika
2

Problem leży poza stertą, więc najlepszymi kandydatami są:

JNI leak  
Allocation of direct memory buffer

Ponieważ ograniczyłeś rozmiar bufora bezpośredniego, moim zdaniem najlepszym kandydatem jest wyciek JNI.

Wacław
źródło
1

Istnieje przydatne narzędzie do przeglądania alokacji pamięci sterty zawartej w JDK o nazwie jmap, a do tego masz również stos itp. (Xss). Uruchom te dwa polecenia jmap, aby uzyskać więcej informacji na temat użycia pamięci:

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

Aby uzyskać jeszcze więcej informacji, możesz połączyć się z procesem za pomocą jconsole (również zawartej w JDK). Jconsole wymaga jednak skonfigurowania JMX w aplikacji.

HampusLi
źródło
Dziękuję za odpowiedź, ale problem wydaje się być gdzieś poza stertą. Zaktualizuję górny post, aby odzwierciedlić niektóre informacje z jmap
Konstantin S.,
Ile wątków ma ten proces? Rozmiar stosu to domyślnie 2 MB na większości platform, więc pomnóż go przez liczbę wątków. Byłbym zaskoczony, gdyby stanowiło to całą „brakującą” pamięć, ale prawdopodobnie część.
HampusLi
Około 300 wątków, wziętych z PMAP, mamy rozmiar stosu 1Mb. I z tego samego wyjścia pmap nie wygląda na to, że którykolwiek z tych stosów używa więcej niż 100 KB
Konstantin S.
0

Użyj JVisualVM. Ma różne widoki, które pokażą, ile pamięci pamięci jest w użyciu, PermGen i tak dalej.

Co do odpowiedzi na twoje pytanie. Java traktuje pamięć zupełnie inaczej niż można się spodziewać.

Kiedy ustawiasz parametry -Xms i -Xmx, mówisz JVM, ile pamięci powinno ono przeznaczyć na stosie na początek, a także ile powinno ono przeznaczyć maksymalnie.

Jeśli masz aplikację Java, która zużyła w sumie 1m pamięci, ale przeszła w -Xms256m -Xmx2g, wówczas JVM zainicjuje się z użyciem 256m pamięci. Nie zużyje mniej niż to. Nie ma znaczenia, że ​​aplikacja wykorzystuje tylko 1m pamięci.

Po drugie. W powyższym przypadku, jeśli aplikacja w pewnym momencie zużywa więcej niż 256 m pamięci, JVM przydzieli tyle pamięci, ile jest konieczne do obsługi żądania. Nie spowoduje to jednak zmniejszenia wielkości sterty do wartości minimalnej. Przynajmniej nie w większości przypadków.

W twoim przypadku, ponieważ ustawiasz minimalną i maksymalną pamięć na 2 g, JVM przydzieli 2 g na początku i utrzyma ją.

Zarządzanie pamięcią Java jest dość złożone, a dostrajanie wykorzystania pamięci może być zadaniem samym w sobie. Istnieje jednak wiele zasobów, które mogą pomóc.

drone.ah
źródło