Pamięć stosu i miejsce na sterty w Javie

1. Wstęp

Aby uruchomić aplikację w optymalny sposób, JVM dzieli pamięć na pamięć stosu i sterty. Za każdym razem, gdy deklarujemy nowe zmienne i obiekty, wywołujemy nową metodę, deklarujemy String lub wykonujemy podobne operacje, JVM wyznacza pamięć do tych operacji z pamięci stosu lub przestrzeni sterty.

W tym samouczku omówimy te modele pamięci. Podamy kilka kluczowych różnic między nimi, sposób ich przechowywania w pamięci RAM, funkcje, które oferują i gdzie ich używać.

2. Pamięć stosu w Javie

Pamięć stosu w Javie służy do statycznej alokacji pamięci i wykonywania wątku. Zawiera prymitywne wartości, które są specyficzne dla metody i odwołania do obiektów znajdujących się na stercie, do których odwołuje się metoda.

Dostęp do tej pamięci jest w kolejności Last-In-First-Out (LIFO). Za każdym razem, gdy wywoływana jest nowa metoda, tworzony jest nowy blok na szczycie stosu, który zawiera wartości specyficzne dla tej metody, takie jak zmienne pierwotne i odniesienia do obiektów.

Gdy metoda zakończy wykonywanie, odpowiadająca jej ramka stosu jest opróżniana, przepływ wraca do metody wywołującej, a miejsce staje się dostępne dla następnej metody.

2.1. Kluczowe cechy pamięci stosu

Oprócz tego, co omówiliśmy do tej pory, poniżej przedstawiono kilka innych funkcji pamięci stosu:

  • Rośnie i kurczy się w miarę wywoływania i zwracania nowych metod
  • Zmienne wewnątrz stosu istnieją tylko tak długo, jak długo działa metoda, która je utworzyła
  • Jest automatycznie przydzielany i zwalniany po zakończeniu wykonywania metody
  • Jeśli ta pamięć jest pełna, Java zgłasza java.lang.StackOverFlowError
  • Dostęp do tej pamięci jest szybki w porównaniu do pamięci sterty
  • Ta pamięć jest bezpieczna wątkowo, ponieważ każdy wątek działa na swoim własnym stosie

3. Przestrzeń sterty w Javie

Miejsce na stercie w języku Java jest używane do dynamicznego przydzielania pamięci dla obiektów Java i klas JRE w czasie wykonywania . Nowe obiekty są zawsze tworzone w przestrzeni sterty, a odniesienia do tych obiektów są przechowywane w pamięci stosu.

Obiekty te mają dostęp globalny i można uzyskać do nich dostęp z dowolnego miejsca w aplikacji.

Ten model pamięci jest dalej podzielony na mniejsze części zwane generacjami, są to:

  1. Young Generation - tutaj wszystkie nowe obiekty są przydzielane i postarzane. Po zapełnieniu następuje niewielkie wyrzucanie elementów bezużytecznych
  2. Stare lub Tenured Generation - tutaj przechowywane są długo zachowane obiekty. Gdy obiekty są przechowywane w Young Generation, ustawiany jest próg wieku obiektu i po osiągnięciu tego progu obiekt jest przenoszony do starej generacji
  3. Trwałe generowanie - obejmuje metadane JVM dla klas środowiska wykonawczego i metod aplikacji

Te różne części są również omówione w tym artykule - Różnica między JVM, JRE i JDK.

Zawsze możemy manipulować wielkością pamięci sterty zgodnie z naszymi wymaganiami. Aby uzyskać więcej informacji, odwiedź ten powiązany artykuł z Baeldung.

3.1. Kluczowe cechy pamięci Java Heap Memory

Oprócz tego, co omówiliśmy do tej pory, poniżej przedstawiono inne cechy miejsca na stercie:

  • Dostęp do niego uzyskuje się za pomocą złożonych technik zarządzania pamięcią, które obejmują młodą generację, starą lub utrudnioną generację i stałą generację
  • Jeśli miejsce na sterty jest pełne, Java zgłasza błąd java.lang.OutOfMemoryError
  • Dostęp do tej pamięci jest stosunkowo wolniejszy niż do pamięci stosowej
  • Ta pamięć, w przeciwieństwie do stosu, nie jest automatycznie zwalniana. Potrzebuje Garbage Collectora, aby zwolnić nieużywane obiekty, aby zachować efektywność wykorzystania pamięci
  • W przeciwieństwie do stosu, sterta nie jest bezpieczna wątkowo i musi być chroniona przez odpowiednią synchronizację kodu

4. Przykład

W oparciu o to, czego się do tej pory nauczyliśmy, przeanalizujmy prosty kod Java i oceńmy, jak zarządzana jest pamięć:

class Person { int id; String name; public Person(int id, String name) { this.id = id; this.name = name; } } public class PersonBuilder { private static Person buildPerson(int id, String name) { return new Person(id, name); } public static void main(String[] args) { int id = 23; String name = "John"; Person person = null; person = buildPerson(id, name); } }

Przeanalizujmy ten krok po kroku:

  1. Upon entering the main() method, a space in stack memory would be created to store primitives and references of this method
    • The primitive value of integer id will be stored directly in stack memory
    • The reference variable person of type Person will also be created in stack memory which will point to the actual object in the heap
  2. The call to the parameterized constructor Person(int, String) from main() will allocate further memory on top of the previous stack. This will store:
    • The this object reference of the calling object in stack memory
    • The primitive value id in the stack memory
    • The reference variable of String argument name which will point to the actual string from string pool in heap memory
  3. The main method is further calling the buildPerson() static method, for which further allocation will take place in stack memory on top of the previous one. This will again store variables in the manner described above.
  4. However, for the newly created object person of type Person, all instance variables will be stored in heap memory.

This allocation is explained in this diagram:

5. Summary

Before we conclude this article, let's quickly summarize the differences between the Stack Memory and the Heap Space:

Parameter Stack Memory Heap Space
Application Stack is used in parts, one at a time during execution of a thread The entire application uses Heap space during runtime
Size Stack has size limits depending upon OS and is usually smaller then Heap There is no size limit on Heap
Storage Stores only primitive variables and references to objects that are created in Heap Space All the newly created objects are stored here
Order It is accessed using Last-in First-out (LIFO) memory allocation system This memory is accessed via complex memory management techniques that include Young Generation, Old or Tenured Generation, and Permanent Generation.
Life Stack memory only exists as long as the current method is running Heap space exists as long as the application runs
Efficiency Comparatively much faster to allocate when compared to heap Slower to allocate when compared to stack
Allocation/Deallocation Ta pamięć jest automatycznie przydzielana i zwalniana, gdy metoda jest wywoływana i zwracana odpowiednio Miejsce na sterty jest przydzielane, gdy nowe obiekty są tworzone i zwalniane przez Gargabe Collector, gdy nie ma już do nich odwołań

6. Wniosek

Stos i sterta to dwa sposoby przydzielania pamięci przez Javę. W tym artykule zrozumieliśmy, jak działają i kiedy używać ich do tworzenia lepszych programów w języku Java.

Aby dowiedzieć się więcej o zarządzaniu pamięcią w Javie, zajrzyj do tego artykułu tutaj. Omówiliśmy również moduł JVM Garbage Collector, który został pokrótce omówiony w tym artykule.