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
Odpowiedzi:
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.
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.
źródło
Co powiesz na sondę Lamdba ? Między innymi może pokazać awarie użycia pamięci podobne do zrzutów ekranu poniżej:
Czasami
pmap -x your_java_pid
może być również pomocna.źródło
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.
źródło
Problem leży poza stertą, więc najlepszymi kandydatami są:
Ponieważ ograniczyłeś rozmiar bufora bezpośredniego, moim zdaniem najlepszym kandydatem jest wyciek JNI.
źródło
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:
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.
źródło
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.
źródło