Mam problem z aplikacją Java działającą w systemie Linux.
Kiedy uruchamiam aplikację, używając domyślnego maksymalnego rozmiaru sterty (64 MB), widzę za pomocą aplikacji tops, że 240 MB pamięci wirtualnej jest przydzielonych do aplikacji. Powoduje to problemy z niektórymi innymi programami na komputerze, które są względnie ograniczone pod względem zasobów.
O ile rozumiem, zarezerwowana pamięć wirtualna i tak nie będzie używana, ponieważ kiedy osiągniemy limit sterty, OutOfMemoryError
zostanie wyrzucona. Uruchomiłem tę samą aplikację w systemie Windows i widzę, że rozmiar pamięci wirtualnej i rozmiar sterty są podobne.
Czy w każdym razie mogę skonfigurować pamięć wirtualną używaną dla procesu Java w systemie Linux?
Edycja 1 : Problemem nie jest sterty. Problem polega na tym, że jeśli na przykład ustawię stos 128 MB, nadal Linux przydziela 210 MB pamięci wirtualnej, co nigdy nie jest potrzebne. **
Edycja 2 : Używanie ulimit -v
pozwala ograniczyć ilość pamięci wirtualnej. Jeśli ustawiony rozmiar jest niższy niż 204 MB, aplikacja nie będzie działać, mimo że nie potrzebuje 204 MB, tylko 64 MB. Chcę więc zrozumieć, dlaczego Java wymaga tak dużej pamięci wirtualnej. Czy można to zmienić?
Edycja 3 : W systemie, który jest osadzony, działa kilka innych aplikacji. A system ma limit pamięci wirtualnej (od komentarzy, ważnych szczegółów).
źródło
Odpowiedzi:
Jest to od dawna skarga na Javę, ale jest w dużej mierze bez znaczenia i zwykle opiera się na spojrzeniu na niewłaściwe informacje. Zwykłe frazowanie to coś w stylu „Hello World na Javie zajmuje 10 megabajtów! Dlaczego to potrzebne?” Oto sposób, aby Hello World na 64-bitowej maszynie JVM przejął 4 gigabajty ... przynajmniej przez jedną formę pomiaru.
Różne sposoby pomiaru pamięci
W systemie Linux górne polecenie podaje kilka różnych liczb pamięci. Oto, co mówi o przykładzie Hello World:
Sytuacja Menedżera zadań Windows jest nieco bardziej skomplikowana. W systemie Windows XP są kolumny „Wykorzystanie pamięci” i „Rozmiar pamięci wirtualnej”, ale oficjalna dokumentacja milczy na ich temat. Windows Vista i Windows 7 dodają więcej kolumn i są one faktycznie udokumentowane . Spośród nich najbardziej użyteczny jest pomiar „zestawu roboczego”; w przybliżeniu odpowiada sumie RES i SHR w systemie Linux.
Zrozumienie wirtualnej mapy pamięci
Pamięć wirtualna zużywana przez proces jest sumą wszystkiego, co znajduje się na mapie pamięci procesu. Obejmuje to dane (np. Stertę Java), ale także wszystkie biblioteki współdzielone i pliki mapowane w pamięci używane przez program. W systemie Linux możesz użyć polecenia pmap, aby zobaczyć wszystkie rzeczy zmapowane w przestrzeni procesu (od tej pory będę odnosił się tylko do Linuksa, ponieważ tego właśnie używam; jestem pewien, że istnieją równoważne narzędzia do Windows). Oto fragment mapy pamięci programu „Hello World”; cała mapa pamięci ma ponad 100 linii i nie jest niczym niezwykłym posiadanie listy tysiąca linii.
Szybkie wyjaśnienie formatu: każdy wiersz zaczyna się od adresu pamięci wirtualnej segmentu. Po tym następuje rozmiar segmentu, uprawnienia i źródło segmentu. Ten ostatni element to albo plik, albo „anon”, co oznacza blok pamięci przydzielany przez mmap .
Zaczynając od góry, mamy
java
). To jest bardzo małe; wszystko, co robi, to ładowanie do bibliotek współdzielonych, w których przechowywany jest prawdziwy kod JVM.-Xmx
wartości; pozwala to mieć ciągłą stertę. Ta-Xms
wartość jest używana wewnętrznie do określenia, ile sterty jest „w użyciu” podczas uruchamiania programu, oraz do wyzwalania wyrzucania elementów bezużytecznych w miarę zbliżania się tego limitu.StackOverFlowError
. W przypadku prawdziwej aplikacji zobaczysz dziesiątki, jeśli nie setki tych wpisów powtórzonych przez mapę pamięci.Współużytkowane biblioteki są szczególnie interesujące: każda wspólna biblioteka 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 segment bez uprawnień to; Widziałem go tylko na Linuksie x64). Część biblioteki tylko do odczytu może być współużytkowana przez wszystkie procesy korzystające z biblioteki; na przykład
libc
ma 1,5 mln pamięci wirtualnej, którą można współdzielić.Kiedy rozmiar pamięci wirtualnej jest ważny?
Mapa pamięci wirtualnej zawiera wiele rzeczy. Niektóre z nich są tylko do odczytu, niektóre są udostępniane, a niektóre są przydzielane, ale nigdy nie zostały zmienione (np. Prawie wszystkie 4 Gb stosu w tym przykładzie). Ale system operacyjny jest wystarczająco inteligentny, aby załadować tylko to, czego potrzebuje, więc rozmiar pamięci wirtualnej jest w dużej mierze nieistotny.
Ważna jest wielkość pamięci wirtualnej, jeśli używasz 32-bitowego systemu operacyjnego, w którym możesz przydzielić tylko 2 Gb (lub, w niektórych przypadkach, 3Gb) 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 wielkości sterty w celu mapowania pamięci dużego pliku lub utworzenia wielu wątków.
Ale biorąc pod uwagę, że 64-bitowe maszyny są wszechobecne, nie sądzę, że minie dużo czasu, zanim Rozmiar pamięci wirtualnej stanie się zupełnie nieistotną statystyką.
Kiedy Resident Set Size jest ważny?
Rozmiar zestawu rezydentnego to część przestrzeni pamięci wirtualnej, która faktycznie znajduje się w pamięci RAM. Jeśli Twój RSS staje się znaczną częścią całkowitej pamięci fizycznej, być może nadszedł czas, aby zacząć się martwić. Jeśli Twój RSS rośnie i zajmuje całą twoją pamięć fizyczną, a twój system zaczyna się zamieniać, nadszedł czas, aby zacząć się martwić.
Ale RSS również wprowadza w błąd, szczególnie na lekko obciążonej maszynie. System operacyjny nie wymaga wiele wysiłku, aby odzyskać strony używane przez proces. Dzięki temu nie zyskasz zbyt wiele, a możliwość wystąpienia kosztownej usterki strony, jeśli proces dotknie strony w przyszłości. W rezultacie statystyki RSS mogą obejmować wiele stron, które nie są aktywnie używane.
Dolna linia
O ile nie dokonujesz wymiany, nie przejmuj się zbytnio tym, co mówią różne statystyki pamięci. Z zastrzeżeniem, że stale rosnący RSS może wskazywać na pewien wyciek pamięci.
W programie Java znacznie ważniejsze jest zwrócenie uwagi na to, co dzieje się na stosie. Ważna jest łączna ilość zajętego miejsca i można podjąć pewne kroki, aby ją zmniejszyć. Ważniejsze jest to, ile czasu spędzasz na zbieraniu śmieci i jakie części sterty są zbierane.
Dostęp do dysku (tj. Bazy danych) jest drogi, a pamięć tania. Jeśli możesz wymienić jeden na drugi, zrób to.
źródło
Znany jest problem z Javą i glibc> = 2.10 (obejmuje Ubuntu> = 10.04, RHEL> = 6).
Lekarstwem jest ustawienie tego środowiska. zmienna:
Jeśli korzystasz z Tomcat, możesz dodać to do
TOMCAT_HOME/bin/setenv.sh
pliku.W przypadku Docker dodaj to do Dockerfile
Istnieje artykuł IBM na temat ustawiania 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 post na blogu mówi
Istnieje również otwarty błąd JDK JDK-8193521 „glibc marnuje pamięć przy domyślnej konfiguracji”
wyszukaj MALLOC_ARENA_MAX w Google lub SO, aby uzyskać więcej referencji.
Możesz także dostroić inne opcje malloc, aby zoptymalizować pod kątem małej fragmentacji przydzielonej pamięci:
źródło
MALLOC_ARENA_MAX
może spowolnić wzrost pamięci, ale nie rozwiązać problem całkowicie.Ilość pamięci przydzielonej na proces Java jest prawie taka sama, jak się spodziewałbym. Miałem podobne problemy z uruchomieniem Javy w systemach osadzonych / z ograniczoną pamięcią. Uruchamianie dowolnej aplikacji z dowolnymi limitami maszyn wirtualnych lub w systemach, które nie mają wystarczającej ilości zamiany, zwykle się psują. Wydaje się, że taka jest natura wielu nowoczesnych aplikacji, które nie są przeznaczone do użytku w systemach o ograniczonych zasobach.
Masz jeszcze kilka opcji, które możesz spróbować ograniczyć w pamięci JVM. Może to zmniejszyć zużycie pamięci wirtualnej:
Ponadto należy również ustawić wartość -Xmx (maksymalny rozmiar sterty) na wartość możliwie zbliżoną do rzeczywistego szczytowego wykorzystania pamięci przez aplikację. Wierzę, że domyślne zachowanie JVM wciąż podwaja wielkość sterty za każdym razem, gdy rozszerza ją do maksimum. Jeśli zaczniesz od stosu 32 mln, a Twoja aplikacja osiągnie maksymalny poziom 65 mln, wówczas stos zwiększy się do 32 mln -> 64 mln -> 128 mln.
Możesz także spróbować to zrobić, aby maszyna wirtualna była mniej agresywna w kwestii powiększania stosu:
Ponadto, z tego, co pamiętam z eksperymentowania z tym kilka lat temu, liczba załadowanych bibliotek natywnych miała ogromny wpływ na minimalny rozmiar. Ładowanie java.net.Socket dodał więcej niż 15 mln, jeśli dobrze pamiętam (i prawdopodobnie nie).
źródło
Sun JVM wymaga dużo pamięci dla HotSpot i mapuje w bibliotekach wykonawczych we wspólnej pamięci.
Jeśli problem dotyczy pamięci, rozważ użycie innej maszyny JVM odpowiedniej do osadzania. IBM ma wersję j9 i istnieje „jamvm” Open Source, który korzysta z bibliotek ścieżek klas GNU. Również Sun ma Squeak JVM działający na SunSPOTS, więc istnieją alternatywy.
źródło
Po prostu myślałem, ale można sprawdzić wpływ na
ulimit -v
opcję .To nie jest rzeczywiste rozwiązanie, ponieważ ograniczyłoby przestrzeń adresową dostępną dla całego procesu, ale pozwoliłoby to sprawdzić zachowanie aplikacji przy ograniczonej pamięci wirtualnej.
źródło
Jednym ze sposobów zmniejszenia rozmiaru stosu w systemie o ograniczonych zasobach może być zabawa ze zmienną -XX: MaxHeapFreeRatio. Zazwyczaj jest to 70 i jest to maksymalny procent sterty, która jest wolna, zanim GC ją zmniejszy. Ustawienie go na niższą wartość, a zobaczysz np. W profilu jvisualvm, że w twoim programie zwykle używana jest mniejsza liczba kupek.
EDYCJA: Aby ustawić małe wartości dla -XX: MaxHeapFreeRatio, musisz również ustawić -XX: MinHeapFreeRatio np.
EDYCJA 2: Dodano przykład prawdziwej aplikacji, która uruchamia się i wykonuje to samo zadanie, jedno z parametrami domyślnymi, a drugie z parametrami 10 i 25. Nie zauważyłem żadnej rzeczywistej różnicy prędkości, chociaż java teoretycznie powinna poświęcić więcej czasu na zwiększenie stosu w drugim przykładzie.
Na koniec maksymalna sterta to 905, używana sterta to 378
Na koniec maksymalna sterta to 722, używana sterta to 378
To faktycznie ma pewien niedokładny charakter, ponieważ nasza aplikacja działa na serwerze zdalnego pulpitu i wielu użytkowników może ją uruchomić jednocześnie.
źródło
Java 1.4 firmy Sun ma następujące argumenty do kontrolowania wielkości pamięci:
http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html
Java 5 i 6 mają trochę więcej. Zobacz http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
źródło
Nie, nie możesz skonfigurować ilości pamięci wymaganej przez VM. Należy jednak pamiętać, że jest to pamięć wirtualna, nie rezydentna, więc pozostaje tam bez szkody, jeśli nie jest faktycznie używana.
Alternatywnie możesz wypróbować inną maszynę JVM niż Sun, o mniejszej pojemności pamięci, ale nie mogę tu doradzić.
źródło