Dlaczego wielkość sterty jest ustalona w maszynach JVM?

20

Czy ktoś może mi wyjaśnić, dlaczego maszyny JVM (nie sprawdziłem ich zbyt wiele, ale nigdy nie widziałem takiego, który nie zrobiłby tego w ten sposób) muszą działać na stałym rozmiarze sterty? Wiem, że łatwiej jest wdrożyć na prostej, ciągłej sterty, ale Sun JVM ma już ponad dekadę, więc spodziewam się, że mieli czas, aby to poprawić.

Konieczność zdefiniowania maksymalnego rozmiaru pamięci programu podczas uruchamiania wydaje się być czymś w stylu lat 60., a potem są złe interakcje z zarządzaniem pamięcią wirtualną systemu operacyjnego (pobieranie GC zamieniło dane, niemożność ustalenia, ile pamięci jest proces Java naprawdę korzystający ze strony systemu operacyjnego, zmarnowane ogromne ilości miejsca na VM (wiem, że nie obchodzi cię twoje wymyślne 48-bitowe maszyny ...)). Sądzę również, że różne smutne próby budowy małych systemów operacyjnych w JVM (serwery aplikacji EE, OSGi) są przynajmniej częściowo winne za tę okoliczność, ponieważ uruchamianie wielu procesów Java w systemie niezmiennie prowadzi do zmarnowanych zasobów, ponieważ musisz daj każdemu z nich pamięć, której może potrzebować w szczycie.

Co zaskakujące, Google nie wywołało burzy oburzenia z powodu tego, czego się spodziewałem, ale mogły zostać pochowane pod milionami ludzi, którzy dowiadują się o ustalonej wielkości sterty i akceptują to.

themel
źródło
Projektowanie serwerów aplikacji EE miałoby doskonały sens nawet bez tej „okoliczności”, ponieważ sama JVM potrzebuje trochę miejsca, a przełączanie między wątkami jest tańsze niż przełączanie między procesami - to jedna z rzeczy, które uczyniły Javę dużą w późnych latach 90-tych.
Michael Borgwardt,
To miłe zdanie i zastanawiam się, ile myśli w to włożyłeś. Na przykład, jak zmieniłaby się interakcja GC / swap, gdybyś nie miał limitu sterty? Jak marnowana jest przestrzeń wirtualna? Otrzymujesz przestrzeń 2 / 3Gb, niezależnie od tego, czy z niej korzystasz, czy nie, a jeśli przekraczasz granice tego miejsca, nie ma znaczenia, czy masz stałą, czy zmienną stertę. Jeśli o to chodzi, w jaki sposób wiele maszyn JVM marnuje cokolwiek innego niż swap (które należy odpowiednio skonfigurować do celów maszyny)?
kdgregory,
2
Jest to rodzaj rant, ale opiera się na wieloletnim doświadczeniu w pisaniu i obsłudze platformy opartej na Javie. Jeśli nie wykonasz zamiany (ponieważ spowoduje to brak reakcji systemu na 20 minut, aż w niekontrolowanym procesie zabraknie miejsca do przydzielenia) i zostanie wyłączona nadmierna pamięć ze względu na stabilność (zabójca OOM nie jest zbyt dobry w wybieraniu ofiar ), zależy Ci na przestrzeni VM i wbrew temu, co sugerują ludzie poniżej, uruchomienie wirtualnej maszyny Java z -Xmx2048m natychmiast przydzieli 2 GB pamięci wirtualnej (przynajmniej w moim Sun JVM w systemie Linux) dla programu z jedną zmienną.
themel
Doskonałe pytanie. Zastanawiam się nad tym samym. Ale który z „faktów” przedstawionych tutaj w q i odpowiedzi są prawidłowe?
Martin Ba,
Po reakcje, których szukałeś, po prostu mosey wraz z modułem śledzenia błędów słonecznych ... np. Tutaj , tutaj i tutaj . Przeczytaj je i poczuj oburzony gniew :)
Podstawowy

Odpowiedzi:

23

Mylisz się. Rozmiar sterty maszyn JVM nie jest stały, a ograniczony:

  • -Xmx ustawia maksymalny rozmiar pamięci sterty
  • -Xms ustawia minimalny rozmiar pamięci sterty

Ustawienie górnej granicy jest konieczne z kilku powodów. Po pierwsze, informuje śmieciarza, kiedy ma zacząć działać. Po drugie, zapobiega zatykaniu się maszyny przez JVM przez zużywanie zbyt dużej ilości pamięci. Minimalna wielkość sterty jest prawdopodobnie przydatna do zarezerwowania takiej ilości pamięci, jakiej program potrzebuje, aby zapobiec wyczerpaniu się pamięci (ponieważ inne procesy zużywają zbyt dużo).

użytkownik 281377
źródło
9
nie, min jest ustawieniem domyślnym, aby uniknąć powolnego uruchamiania, gdy dużo trzeba przydzielić, co powoduje powtarzające się wzrosty sterty, stronicowanie poradzi sobie z wyczerpaniem fizycznego pamięci RAM
maniak zapadkowy
@ratchetfreak to była moja druga
próba
@ user281377 Jeśli tak jest, to w jaki sposób C # może działać poprawnie bez maksymalnego rozmiaru pamięci sterty?
cmorse
cmorse: Mogę tylko zgadywać. Być może Java jest przeznaczona dla dużych serwerów, na których wiele aplikacji ma wspólne zasoby i pożądane są ściśle egzekwowane limity, podczas gdy .net jest raczej stworzony dla komputerów PC i mniejszych, bardziej dedykowanych serwerów.
user281377,
@ user281377: Aplikacje Java, z których korzystałem, zabrakło miejsca na sterty, ogólnie radzą sobie z tym bardzo słabo, zazwyczaj po prostu ulegają awariom lub są bardzo płatkowe. A ASP.net działa dobrze zarówno na dużych, jak i małych serwerach. Naprawdę nie rozumiem, dlaczego domyślnie Java wymusza ten limit. Chciałbym usłyszeć z uzasadnienia ich decyzji ... Jestem pewien, że mieli dobry powód.
cmorse
6

Wyobrażam sobie, że odpowiedź ma coś wspólnego z dziedzictwem Javy. Został pierwotnie zaprojektowany jako język używany w systemach wbudowanych, w których oczywiście zasoby są ograniczone i nie chcesz, aby procesy po prostu pochłaniały wszystko, co jest dostępne. Pomaga również w administrowaniu systemem, ponieważ ułatwia ustawienie zasobów na serwerze, jeśli można ustawić limity zasobów. Widzę, że najnowsze maszyny JVM wydają się używać wielu nieciągłych stert, chociaż oczywiście wszystko to pojawia się jako jedna sterty w kodzie.

(FWIW musiałeś określić wymagania dotyczące pamięci programu w wersjach MacOS sprzed Apple Darwina (i tak do wersji System 7, która była ostatnią, której używałem), która była już w latach 80.)

TMN
źródło
1
+1 - za to, że jest (1) jedyną odpowiedzią, która faktycznie dotyczyła pytania, i (2) jest wiarygodna.
kdgregory,
2

Musisz dać GC jakiś mechanizm, który poinformuje go, kiedy ma zostać uruchomiony, w przeciwnym razie twój program zapełni całą pamięć wirtualną. Istnieje wiele alternatywnych sposobów wyzwalania GC: upływający czas, liczba przydziałów, liczba przydziałów, prawdopodobnie inne, o których nie mogę teraz myśleć. IMO żaden z nich nie jest tak dobry, jak po prostu ustawienie granicy pamięci i uruchomienie GC, gdy przydzielone miejsce przekroczy tę granicę.

Kluczem jest ustawienie prawidłowych granic. Patrzę na -msto: „tyle pamięci potrzebuje moja aplikacja” i -mx„nigdy nie powinna przekraczać tej ilości”. W przypadku wdrożenia produkcyjnego oba powinny być blisko siebie, jeśli nie równe, i powinny być oparte na rzeczywistych, zmierzonych wymaganiach.

Twoje obawy dotyczące „zmarnowanej” pamięci wirtualnej są bezpodstawne: jest wirtualna, (prawie) darmowa. Tak, nadanie stertom zbyt dużego przydziału oznacza, że ​​nie można uruchomić tylu wątków ani załadować tylu plików mapowanych w pamięci. Ale to część projektowania aplikacji: masz ograniczone zasoby, musisz podzielić je na partycje w sposób umożliwiający uruchomienie aplikacji. W stosie „w stylu C”, który będzie się rozwijał, aż osiągniesz szczyt pamięci, podstawowy problem jest taki sam, po prostu nie musisz o tym myśleć, dopóki nie będziesz miał kłopotów.

Jedyną rzeczą, którą duża sterty może „zmarnować”, jest przestrzeń wymiany, ponieważ wszystkie zapisywalne segmenty wymagają zaangażowania wymiany. Ale to część projektowania systemu: jeśli chcesz mieć wiele JVM działających na tym samym urządzeniu, zwiększ swap lub zmniejsz przydział sterty. Jeśli zaczną szarpać, próbujesz zrobić zbyt wiele z systemem; kup więcej pamięci (a jeśli nadal korzystasz z procesora 32-bitowego, kup nowe pudełko).

kdgregory
źródło
1
Ale próg uruchomienia GC nie musi mieć nic wspólnego ze stałym progiem, którego całkowite wykorzystanie pamięci przez program nie może przekroczyć. (Rzeczywiście prawdopodobnie nie powinno; jeśli masz dużą stertę i możesz GC tylko wtedy, gdy jest pełny, koniecznie masz rzadkie, ale długie okresy GC). Zobacz generowanie odśmiecania.
Ben
@Ben - tak, masz rację. A moje drugie zdanie wskazuje, że istnieją alternatywy. Nie zgadzam się jednak, że kupa o stałej wielkości jest niewłaściwym sposobem zarządzania GC w ogólnym przypadku. Prawidłowo dostrojona maszyna JVM używa „właściwej” wielkości sterty; z mojego doświadczenia wynika, że ​​długie przerwy GC zdarzają się, gdy JVM nie jest odpowiednio dostrojony. I często „odpowiedni” rozmiar sterty jest znacznie mniejszy, niż mogłoby się wydawać.
kdgregory,
-2

Jak mówi user281377, określasz tylko górną granicę ilości pamięci, którą proces może zużyć. Oczywiście sama aplikacja zajmie tylko tyle miejsca, ile potrzebuje.

To, czy powinna istnieć domyślna górna granica, czy nie, to kolejne pytanie, zarówno z pro, jak i przeciw.

Dagnele
źródło