Zarządzanie pamięcią w Java
⚡ Inteligentne podsumowanie
Zarządzanie pamięcią w Java wyjaśnia, w jaki sposób JVM dzieli pamięć wykonawczą na stos, stertę, Codei regiony statyczne, sposób przepływu odwołań do obiektów podczas wywołań metod oraz sposób, w jaki Garbage Collector odzyskuje niedostępne obiekty, aby aplikacje pozostały stabilne i wolne od wycieków pamięci.

Czym jest pamięć stosowa? Java?
Pamięć stosowa w Java to obszar pamięci JVM, który przechowuje ramki metod, zmienne lokalne i zmienne referencyjne dla każdego wątku. Dostęp do stosu jest zawsze uzyskiwany w kolejności „ostatnia wejściowa-pierwsza wyjściowa”, więc ostatnio wywołana metoda znajduje się na górze, a jej zmienne lokalne są umieszczane i usuwane wraz z ramką.
Każdy wątek w Java Maszyna wirtualna otrzymuje własny stos, który z założenia zapewnia izolację wywołań metod i bezpieczeństwo wątków. Prymitywne zmienne lokalne, takie jak int, boolean i double, znajdują się bezpośrednio w ramce, podczas gdy odwołania do obiektów przechowywane na stosie wskazują na obiekty przydzielone na stercie.
Czym jest pamięć sterty Java?
Pamięć sterty to współdzielony obszar JVM, w którym przechowywane są wszystkie Java Obiekt i tablica utworzone za pomocą operatora new, wraz z wszelkimi zmiennymi referencyjnymi należącymi do tych obiektów jako pola instancji. W przeciwieństwie do stosu, sterta jest współdzielona przez wszystkie wątki, dlatego dostęp do obiektów często wymaga synchronizacji.
Sterta to obszar zarządzany przez Garbage Collector. Nowoczesne maszyny wirtualne Java HotSpot dzielą stertę na młodą generację dla obiektów krótkowiecznych i starą generację dla obiektów długowiecznych, a metadane klas są przechowywane w oddzielnym, natywnym regionie zwanym Metaspace.
Alokacja pamięci w Java
Alokacja pamięci w Java to proces, w którym JVM rezerwuje wirtualne obszary pamięci dla zmiennych oraz instancji klas i struktur podczas wykonywania programu. Pamięć nie jest przydzielana obiektowi podczas deklaracji; tworzona jest jedynie referencja. Faktyczna alokacja obiektów odbywa się za pomocą operatora new, więc każdy obiekt znajduje się na stercie.
Java alokacja pamięci podzielona jest na następujące sekcje:
- kupa
- Stos
- Code
- Statyczny
Podział pamięci jest konieczny do efektywnego zarządzania środowiskiem wykonawczym.
- Code sekcja zawiera skompilowane przez Ciebie kod bajtowy.
- Stos sklepy sekcyjne metody, zmienne lokalne i zmienne referencyjne.
- kupa sekcja zawiera obiekty i może również zawierać zmienne referencyjne przechowywane jako pola instancji.
- Statyczny sekcja posiada dane statyczne i metody statyczne współdzielone przez wszystkie instancje.
Różnica między zmienną lokalną a zmienną instancji
Zrozumienie, gdzie w JVM znajduje się każdy rodzaj zmiennej, pomaga wyjaśnić zachowanie stosu i sterty. zmienna instancji jest zadeklarowane wewnątrz klasy, ale poza jakąkolwiek metodąi znajduje się na stercie jako część jej obiektu.
class Student{ int num; // num is instance variable public void showData{}
A zmienna lokalna jest zadeklarowane wewnątrz metody, włączając argumenty metodyi znajduje się na stosie wewnątrz aktywnej ramki.
public void sum(int a){ int x = a + 3; // a, x are local variables }
Różnica między stosem a stertą
Stos i sterta rozwiązują różne problemy w JVM. Stos zapewnia każdemu wątkowi szybką, deterministyczną alokację danych krótkotrwałych powiązanych z zakresem metody, podczas gdy sterta zapewnia współdzielony obszar dla obiektów długotrwałych, do którego może odwoływać się każdy wątek. Krótki film poniżej podsumowuje różnicę przed przejściem do kolejnego etapu.
Kliknij w tym miejscu jeśli film nie jest dostępny
Aby zobaczyć jak stos i sterta ze sobą współpracują, rozważmy metodę główną, która wywołuje metodę m1.
public void m1{ int x = 20; }
W stosie JVM tworzona jest ramka dla metody m1.
Zmienna x w m1 jest również tworzona w ramce dla m1 na stosie, jak pokazano na poniższym obrazku.
Metoda m1 wywołuje następnie metodę m2. W stosie tworzona jest nowa ramka dla m2 na ramce dla m1.
Zmienne lokalne b i c są również tworzone wewnątrz ramki dla m2 na stosie.
public void m2(int b){ boolean c; }
Następnie m2 wywołuje metodę m3. Ponownie, na szczycie stosu tworzona jest ramka dla m3, jak pokazano poniżej.
Teraz powiedzmy, że metoda m3 tworzy obiekt dla klasy Konto, która ma dwa zmienne instancji int p i int q.
class Account { int p; int q; }
Oto kod metody m3.
public void m3(){ Account ref = new Account(); // more code }
Polecenie new Account() tworzy obiekt Account na stercie.
Zmienna odniesienia ref jest tworzona na stosie wewnątrz ramki dla m3.
Operator przypisania sprawia, że zmienna referencyjna wskazuje na obiekt w stercie.
Po zakończeniu wykonywania metody sterowanie powraca do metody wywołującej, w tym przypadku jest to metoda m2.
Ramka dla metody m3 jest usuwana ze stosu.
Ponieważ zmienna referencyjna nie wskazuje już na obiekt na stercie, obiekt ten kwalifikuje się do zbierania śmieci.
Po zakończeniu działania metody m2 zostaje ona usunięta ze stosu, a wszystkie jej zmienne stają się niedostępne. To samo dzieje się z metodą m1, a sterowanie ostatecznie wraca do metody głównej.
Co się stanie, jeśli obiekt będzie zawierał inną referencję jako swoją zmienną instancyjną?
public static void main(String args[]) { A parent = new A(); // more code } class A { B child = new B(); int e; } class B { int c; int d; }
W tym przypadku zmienna referencyjna child znajduje się na stercie jako część obiektu A i z kolei wskazuje na własny obiekt B, jak pokazano poniżej.
Na czym polega zbieranie śmieci Java?
Zbiórka śmieci w Java to proces, w którym JVM automatycznie zarządza pamięcią. Garbage Collector wyszukuje obiekty, do których nie można już dotrzeć z żadnego aktywnego odwołania i odzyskuje ich pamięć. Dynamiczna alokacja pamięci odbywa się za pomocą operatora new, a pamięć pozostaje przydzielona, dopóki program nie przestanie posiadać odwołania do obiektu.
Gdy nie ma już żadnych odniesień, obiekt uznaje się za niepotrzebny, a zajmowaną przez niego pamięć można odzyskać. Nie ma wyraźnej potrzeby niszczenia obiektu, ponieważ Java automatycznie obsługuje dealokację za pomocą Garbage Collectora.
Technika, która za tym stoi, jest znana jako Zbieranie śmieciProgramy, które nie zwalniają pamięci, ostatecznie ulegają awarii, gdy nie pozostaje nic do przydzielenia. Mówi się, że takie programy mają wycieki pamięci. Zbiórka śmieci w Java działa automatycznie przez cały okres istnienia programu, co eliminuje konieczność ręcznego zwalniania uprawnień i zmniejsza ryzyko wycieków.
W C natomiast programista jest odpowiedzialny za zwalnianie pamięci przydzielonej dynamicznie za pomocą funkcji free(). W tym miejscu Java zarządzanie pamięcią daje dużą przewagę.
Nowoczesne maszyny wirtualne Java HotSpot zawierają kilka modułów Garbage Collector dostosowanych do różnych obciążeń. Domyślny moduł Garbage Collector G1 dąży do zrównoważenia przepustowości i czasu pauzy na stosach wielogigabajtowych. ZGC i Shenandoah dążą do pauz poniżej milisekundy na bardzo dużych stosach, co czyni je…tracOptymalny dla usług wrażliwych na opóźnienia. Wybór odpowiedniego kolektora i dostrojenie rozmiarów Młodej Generacji, Starej Generacji i Metaspace to podstawowa umiejętność Java praca performatywna.
Uwaga: Wszystkie obiekty są tworzone w sekcji pamięci Heap, która jest obszarem zarządzanym przez Garbage Collector.
Przykład: Aby nauczyć się mechanizmu zbierania śmieci w Java
W tym przewodniku pokazano, kiedy odniesienia kwalifikują się do odśmiecania w prostym programie.
Krok 1) Skopiuj poniższy kod do edytora.
class Student{ int a; int b; public void setData(int c,int d){ a=c; b=d; } public void showData(){ System.out.println("Value of a = "+a); System.out.println("Value of b = "+b); } public static void main(String args[]){ Student s1 = new Student(); Student s2 = new Student(); s1.setData(1,2); s2.setData(3,4); s1.showData(); s2.showData(); //Student s3; //s3=s2; //s3.showData(); //s2=null; //s3.showData(); //s3=null; //s3.showData(); } }
Krok 2) Zapisz, skompiluj i uruchom kod. Jak pokazano na diagramie, tworzone są dwa obiekty i dwie zmienne referencyjne.
Krok 3) Odkomentuj wiersze 20, 21 i 22. Zapisz, skompiluj i uruchom kod.
Krok 4) Jak pokazano na poniższym diagramie, dwie zmienne odniesienia wskazują teraz na ten sam obiekt.
Krok 5) Odkomentuj wiersze 23 i 24. Zapisz, skompiluj i uruchom kod.
Krok 6) Jak pokazano poniżej, s2 staje się nullem, ale s3 nadal wskazuje na obiekt, więc obiekt nie kwalifikuje się jeszcze do odśmiecania.
Krok 7) Odkomentuj wiersze 25 i 26. Zapisz, skompiluj i uruchom kod.
Krok 8) W tym momencie żadne odniesienia nie wskazują na obiekt, więc kwalifikuje się on do odśmiecania. Odśmiecacz usuwa go z pamięci i nie ma możliwości jego odzyskania.
Jak usunąć obiekt w Java?
Java nie zapewnia operatora ręcznego usuwania, więc standardowym podejściem jest usuwanie wszystkich odniesień do obiektu, tak aby Garbage Collector mógł go odzyskać.
1) Aby obiekt kwalifikował się do odśmiecania, przypisz każdej zmiennej referencyjnej, która do niego wskazuje, wartość null.
2) Typy prymitywne nie są obiektami, więc nie można im przypisać wartości null. Ich pamięć jest automatycznie odzyskiwana po usunięciu otaczającej ramki stosu.


















