Wykorzystanie pamięci wirtualnej z Javy pod Linuksem, za dużo pamięci

Mam problem z aplikacją Java działającą pod Linuksem.

Kiedy uruchamiam aplikację, używając domyślnego maksymalnego rozmiaru sterty (64 MB), widzę w aplikacji tops, że 240 MB pamięci wirtualnej jest przydzielone do aplikacji. Stwarza to pewne problemy z innym oprogramowaniem na komputerze, które jest stosunkowo ograniczone zasobami.

Zarezerwowana pamięć wirtualna i tak nie zostanie użyta, o ile rozumiem, ponieważ gdy osiągniemy limit sterty, OutOfMemoryError jest rzucony. Uruchomiłem tę samą aplikację pod windows i widzę, że rozmiar pamięci wirtualnej i rozmiar sterty są podobne.

Czy w ogóle mogę skonfigurować pamięć wirtualną używaną dla procesu Javy pod Linuksem?

Edit 1 : problemem nie jest sterta. Problem polega na tym, że jeśli ustawię stertę 128 MB, na przykład, nadal Linux przydziela 210 MB pamięci wirtualnej, która nigdy nie jest potrzebna.**

Edit 2: Użycie ulimit -v pozwala ograniczyć ilość pamięć wirtualna. Jeśli rozmiar jest mniejszy niż 204 MB, to aplikacja nie będzie działać, nawet jeśli nie potrzebuje 204 MB, tylko 64 MB. Chcę więc zrozumieć, dlaczego Java wymaga tak dużej ilości pamięci wirtualnej. Czy można to zmienić?

Edit 3: w systemie działa kilka innych aplikacji, które są osadzone. A system ma limit pamięci wirtualnej (od komentarzy, ważnych szczegółów).

Author: Lii, 2009-02-18

8 answers

[7]}to była długotrwała Skarga na Javę, ale w dużej mierze bez znaczenia i zwykle opiera się na patrzeniu na błędne informacje. Zwykłe frazowanie to coś w stylu " Hello World na Javie zajmuje 10 megabajtów! Po co mu to?"Oto sposób, aby Hello World NA 64-bitowym JVM przejął 4 gigabajty ... przynajmniej jedną formą pomiaru.

java -Xms1024m -Xmx4096m com.example.Hello

Różne sposoby pomiaru pamięci

W Linuksie polecenie top daje Ci kilka różne liczby dla pamięci. Oto, co mówi o przykładzie 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 jest wirtualną przestrzenią pamięci: sumą wszystkiego na mapie pamięci wirtualnej (patrz niżej). Jest w dużej mierze bez znaczenia, z wyjątkiem sytuacji, gdy nie jest (patrz poniżej).
  • RES to resident set size: liczba stron, które są aktualnie rezydentne w pamięci RAM. W prawie wszystkich przypadkach jest to jedyna Liczba, której powinieneś użyć, gdy mówisz " zbyt duży."Ale to wciąż nie jest zbyt dobra liczba, szczególnie mówiąc o Javie.
  • SHR to ilość pamięci rezydentnej współdzielona z innymi procesami. W przypadku procesu Java jest to zwykle ograniczone do bibliotek współdzielonych i plików JARfiles mapowanych w pamięci. W tym przykładzie miałem uruchomiony tylko jeden proces Javy, więc podejrzewam, że 7K jest wynikiem bibliotek używanych przez system operacyjny.
  • SWAP nie jest domyślnie włączony i nie jest pokazywany tutaj. Wskazuje ilość pamięci wirtualnej, która jest aktualnie rezydentna na dysku, czy jest aktualnie w przestrzeni wymiany . System Operacyjny jest bardzo dobry w utrzymywaniu aktywnych stron w pamięci RAM, a jedynymi lekarstwami na zamianę są (1) kup więcej pamięci lub (2) zmniejsz liczbę procesów, więc najlepiej zignorować tę liczbę.

Sytuacja w Menedżerze Zadań systemu Windows jest nieco bardziej skomplikowana. W systemie Windows XP istnieją kolumny "użycie pamięci" i "rozmiar pamięci wirtualnej", ale oficjalna dokumentacja milczy na temat ich znaczenia. Windows Vista i Windows 7 dodaj więcej kolumny, a właściwie udokumentowane . Z nich pomiar "zestawu roboczego" jest najbardziej użyteczny; w przybliżeniu odpowiada sumie RES i SHR na Linuksie.

Zrozumienie mapy pamięci wirtualnej

Pamięć wirtualna zużywana przez proces jest sumą wszystkiego, co znajduje się na mapie pamięci procesu. Obejmuje to dane (np. stertę Javy), ale także wszystkie biblioteki współdzielone i pliki mapowane pamięcią używane przez program. Na Linuksie możesz użyć PMAP polecenie, aby zobaczyć wszystkie rzeczy zmapowane do przestrzeni procesowej(od teraz będę się odnosił tylko do Linuksa, bo tego właśnie używam; jestem pewien, że istnieją równoważne Narzędzia Dla Windows). Oto fragment mapy pamięci programu "Hello World"; cała mapa pamięci ma ponad 100 linii i nie jest niczym niezwykłym lista licząca tysiąc linii.
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
...

Szybkie wyjaśnienie formatu: każdy wiersz zaczyna się od adresu pamięci wirtualnej segmentu. To jest następnie rozmiar segmentu, uprawnienia i źródło segmentu. Ta ostatnia Pozycja to plik lub "anon", który wskazuje blok pamięci przydzielony przez mmap.

Zaczynając od góry, mamy

  • JVM loader (czyli program uruchamiany po wpisaniu java). To jest bardzo małe; wszystko, co robi, to ładowanie w bibliotekach współdzielonych, w których przechowywany jest prawdziwy kod JVM.
  • Banda bloków anon przechowujących stertę Javy i wewnętrzne dane. To jest Sun JVM, więc sterta jest podzielona na wiele generacji, z których każda jest własnym blokiem pamięci. Zauważ, że JVM przydziela przestrzeń pamięci wirtualnej na podstawie wartości -Xmx; dzięki temu może mieć przylegającą stertę. Wartość -Xms jest używana wewnętrznie, aby powiedzieć, ile sterty jest "w użyciu", gdy program się uruchamia, i aby wyzwalać usuwanie śmieci, gdy zbliża się ten limit.
  • plik JARfile mapowany pamięcią, w tym przypadku plik zawierający " klasy JDK."Kiedy pamięć-Mapa słoik, ty może uzyskać dostęp do plików w nim bardzo efektywnie(w porównaniu do czytania go od początku za każdym razem). Sun JVM zmapuje wszystkie słoiki na ścieżce classpath; jeśli twój kod aplikacji musi uzyskać dostęp do JAR, możesz również zmapować pamięć.
  • dane dla dwóch wątków. Blok 1M to stos wątków; Nie wiem co idzie do bloku 4K. W przypadku prawdziwej aplikacji zobaczysz dziesiątki, jeśli nie setki tych wpisów powtarzanych przez mapę pamięci.
  • jedna z bibliotek współdzielonych, która posiada rzeczywisty kod JVM. Jest ich kilka.
  • biblioteka współdzielona dla biblioteki standardowej C. Jest to tylko jedna z wielu rzeczy, które ładuje JVM, które nie są ściśle częścią Javy.

Biblioteki współdzielone są szczególnie interesujące: każda biblioteka współdzielona ma co najmniej dwa segmenty: segment tylko do odczytu zawierający kod biblioteki oraz segment do odczytu i zapisu, który zawiera globalne dane dla poszczególnych procesów dla biblioteki (Nie wiem, co to jest segment bez uprawnień; tylko na Linuksie x64). Część biblioteki tylko do odczytu może być współdzielona między wszystkimi procesami używającymi tej biblioteki; na przykład libc ma 1,5 M przestrzeni pamięci wirtualnej, która może być współdzielona.

Kiedy Rozmiar pamięci wirtualnej jest ważny?

[7]}wirtualna mapa pamięci zawiera wiele rzeczy. Część z nich jest tylko do odczytu, część jest dzielona, a część jest przydzielana, ale nigdy nie dotykana(np. prawie wszystkie 4GB sterty w tym przykładzie). Ale System Operacyjny jest wystarczająco inteligentny, aby ładuj tylko to, czego potrzebuje, więc rozmiar pamięci wirtualnej jest w dużej mierze nieistotny.

Rozmiar pamięci wirtualnej jest ważny, jeśli używasz 32-bitowego systemu operacyjnego, w którym możesz przeznaczyć tylko 2 GB (lub, w niektórych przypadkach, 3 GB) przestrzeni adresowej procesu. W takim przypadku masz do czynienia z ograniczonym zasobem i być może będziesz musiał dokonać kompromisów, takich jak zmniejszenie rozmiaru sterty, aby zmapować duży plik lub utworzyć wiele wątków.

Ale biorąc pod uwagę, że maszyny 64-bitowe są wszechobecne, Nie sądzę, że minie dużo czasu, zanim rozmiar pamięci wirtualnej będzie zupełnie nieistotną statystyką.

Kiedy Rozmiar Resident Set jest ważny?

Resident set size to ta część wirtualnej przestrzeni pamięci, która faktycznie znajduje się w pamięci RAM. Jeśli twój kanał RSS będzie stanowił znaczną część całkowitej pamięci fizycznej, może to być czas, aby zacząć się martwić. Jeśli twój RSS rośnie, aby zająć całą fizyczną pamięć, a system zacznie się wymieniać, to już dawno, aby zacząć się martwić.

Ale RSS również wprowadza w błąd, szczególnie na lekko załadowanej maszynie. System operacyjny nie wymaga wiele wysiłku, aby odzyskać strony używane przez proces. W ten sposób można uzyskać niewielką korzyść, a także potencjał kosztownej usterki strony, jeśli proces dotknie strony w przyszłości. W rezultacie statystyka RSS może zawierać wiele stron, które nie są aktywnie używane.

Bottom Line

Jeśli nie wymieniasz, nie przejmuj się zbytnio tym, co różne statystyki pamięci mówią ci. Z zastrzeżeniem, że stale rosnący RSS może wskazywać na jakiś wyciek pamięci.

W przypadku programu Java o wiele ważniejsze jest zwracanie uwagi na to, co dzieje się w stercie. Całkowita ilość zużywanej przestrzeni jest ważna i istnieje kilka kroków, które możesz podjąć, aby ją zmniejszyć. Ważniejsza jest ilość czasu, który spędzasz na zbieraniu śmieci i które części sterty są zbierane.

Dostęp do dysku (ie, a baza danych) jest droga, a pamięć tania. Jeśli możesz wymienić jedną na drugą, zrób to.

 564
Author: kdgregory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-05 02:01:28

Jest znany problem z Javą i glibc >= 2.10(dotyczy Ubuntu >= 10.04, RHEL >= 6).

/ Align = "left" / zmienna: export MALLOC_ARENA_MAX=4 Jeśli używasz programu Tomcat, możesz dodać go do pliku TOMCAT_HOME/bin/setenv.sh.

Jest artykuł IBM o ustawieniu MALLOC_ARENA_MAX https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en

Ten wpis na blogu mówi

Pamięć rezydentna jest znana z pełzania w sposób podobny do wyciek pamięci lub fragmentacja pamięci.

Szukaj MALLOC_ARENA_MAX w Google lub tak więcej odniesień.

Możesz chcieć dostroić również inne opcje malloc, aby zoptymalizować małą fragmentację przydzielonej pamięci:

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536
 33
Author: Lari Hotari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-21 20:13:17

Ilość pamięci przydzielonej dla procesu Java jest prawie równa temu, czego bym się spodziewał. Miałem podobne problemy z uruchomieniem Javy na systemach embedded/memory limited. Uruchamianie dowolnych aplikacji z dowolnymi limitami maszyn wirtualnych lub na systemach, które nie mają odpowiedniej ilości swapów, Zwykle pęka. Wydaje się, że jest to natura wielu nowoczesnych aplikacji, które nie są przeznaczone do użytku na systemach ograniczonych zasobami.

Masz jeszcze kilka opcji możesz spróbować ograniczyć pamięć JVM odcisk stopy. Może to zmniejszyć rozmiar pamięci wirtualnej:

- XX: ReservedCodeCacheSize = 32m zarezerwowany rozmiar pamięci podręcznej kodu ( w bajtach) - maksymalny rozmiar pamięci podręcznej kodu. [Solaris 64-bit, amd64 i-server x86: 48m; w 1.5.0_06 i wcześniejsze, Solaris 64-bit i and64: 1024M.]

- XX: MaxPermSize = 64m Rozmiar stałego pokolenia. [5.0 i nowsze: 64-bitowe maszyny wirtualne są skalowane o 30% większe; 1.4 amd64: 96m; 1.3.1-klient: 32m.]

Również powinieneś ustawić-Xmx (max wielkość sterty) do wartości jak najbardziej zbliżonej do rzeczywiste zużycie pamięci szczytowej Twojej aplikacji. Myślę, że domyślnym zachowaniem JVM jest nadal podwójne rozmiar stosu za każdym razem, gdy rozszerza go do max. Jeśli zaczniesz od sterty 32M, a Twoja aplikacja osiągnie szczyt 65M, to sterta będzie rosła o 32M - > 64M - > 128M.]} Możesz również spróbować tego, aby vm było mniej agresywne, jeśli chodzi o wzrost sterty:

- XX: MinHeapFreeRatio = 40 Minimum procent kupy za darmo po GC do unikaj ekspansji.

Ponadto, z tego co pamiętam z eksperymentów z tym kilka lat temu, liczba załadowanych bibliotek natywnych miała ogromny wpływ na minimalny ślad. Ładowanie java. net. Socket dodało więcej niż 15M jeśli dobrze pamiętam (a pewnie nie).

 9
Author: James Schek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-18 16:21:52

Sun JVM wymaga dużo pamięci dla hotspota i mapuje w bibliotekach runtime w pamięci współdzielonej.

Jeśli pamięć jest problemem, rozważ użycie innego JVM odpowiedniego do osadzania. IBM ma j9 i jest Open Source "jamvm", który używa bibliotek GNU classpath. Również Słońce ma piszczący JVM działający na plam słonecznych, więc istnieją alternatywy.

 7
Author: Thorbjørn Ravn Andersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-03 17:14:16

To tylko myśl, ale możesz sprawdzić wpływ A ulimit -v Opcja .

To nie jest rzeczywiste rozwiązanie, ponieważ ograniczyłoby przestrzeń adresową dostępną dla wszystkich procesów, ale pozwoliłoby na sprawdzenie zachowania aplikacji z ograniczoną pamięcią wirtualną.

 2
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-18 14:51:39

Jednym ze sposobów zmniejszenia sice sterty systemu o ograniczonych zasobach może być pobawienie się zmienną-XX:MaxHeapFreeRatio. Jest to zwykle ustawione na 70 i jest maksymalnym procentem stosu, który jest wolny, zanim GC go zmniejszy. Ustawienie go na niższą wartość, a zobaczysz w jvisualvm profiler, że mniejszy sice sterty jest zwykle używany do programu.

EDIT: aby ustawić małe wartości dla-XX: MaxHeapFreeRatio musisz również ustawić -XX: MinHeapFreeRatio Eg

java -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=25 HelloWorld

EDIT2: Dodano przykład dla prawdziwej aplikacji, która uruchamia i wykonuje to samo zadanie, jeden z domyślnymi parametrami i jeden z parametrami 10 i 25 jako parametry. Nie zauważyłem żadnej rzeczywistej różnicy prędkości, chociaż java w teorii powinna poświęcić więcej czasu na zwiększenie sterty w tym drugim przykładzie.

Domyślne parametry

Na końcu, maksymalna sterta to 905, używana sterta to 378

MinHeap 10, MaxHeap 25

Na końcu maksymalna sterta to 722, używana sterta to 378

To rzeczywiście ma jakiś wpływ, jako nasz aplikacja działa na serwerze pulpitu zdalnego i wielu użytkowników może ją uruchomić jednocześnie.

 2
Author: runholen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-22 07:35:40

Sun ' s java 1.4 ma następujące argumenty do kontrolowania rozmiaru pamięci:

- Xmsn Określa początkowy rozmiar puli alokacji pamięci w bajtach. Wartość ta musi być wielokrotnością 1024 więcej niż 1MB. Dodaj literę k lub K, aby wskazać kilobajty, lub m lub M aby wskazać megabajty. Default wartość to 2MB. Przykłady:

           -Xms6291456
           -Xms6144k
           -Xms6m

- Xmxn Określa maksymalny rozmiar puli alokacji pamięci w bajtach. Wartość ta musi być wielokrotnością 1024 więcej niż 2MB. Dodaj literę k lub K, aby wskazać kilobajty, lub m lub M aby wskazać megabajty. Default wartość to 64MB. Przykłady:

           -Xmx83886080
           -Xmx81920k
           -Xmx80m

Http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html

Java 5 i 6 mają więcej. Zobacz http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

 1
Author: Paul Tomblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-18 14:35:14

Nie, Nie można skonfigurować ilości pamięci potrzebnej przez maszynę wirtualną. Należy jednak pamiętać, że jest to pamięć wirtualna, a nie rezydentna, więc po prostu pozostaje tam bez szkody, jeśli nie jest faktycznie używana.

Alernatywnie, możesz spróbować innego JVM niż Sun one, z mniejszym śladem pamięci, ale nie mogę tu doradzać.

 0
Author: Marko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-18 14:28:52