Jak rozumiem, w Javie pamięć stosu zawiera prymitywy i wywołania metod, a pamięć sterty służy do przechowywania obiektów.
Załóżmy, że mam klasę
class A {
int a ;
String b;
//getters and setters
}
Gdzie będzie przechowywany prymityw
a
w klasieA
?Dlaczego pamięć sterty w ogóle istnieje? Dlaczego nie możemy przechowywać wszystkiego na stosie?
Czy kiedy obiekt zostanie wyrzucony śmieci, stos skojarzony z przedmiotem jest zniszczony?
Odpowiedzi:
Podstawową różnicą między stosem a stertą jest cykl życia wartości.
Wartości stosu istnieją tylko w zakresie funkcji, w której zostały utworzone. Po zwróceniu są odrzucane.
Wartości sterty istnieją jednak na stosie. Są one tworzone w pewnym momencie i niszczone w innym (przez GC lub ręcznie, w zależności od języka / środowiska wykonawczego).
Teraz Java przechowuje tylko prymityw na stosie. Dzięki temu stos jest mały i pomaga utrzymać małe ramki poszczególnych stosów, umożliwiając w ten sposób więcej zagnieżdżonych wywołań.
Obiekty są tworzone na stercie, a tylko stosy (które z kolei są prymitywami) są przekazywane na stosie.
Jeśli więc utworzysz obiekt, zostanie on umieszczony na stosie wraz ze wszystkimi zmiennymi, które do niego należą, aby mógł pozostać po powrocie wywołania funkcji.
źródło
char
s są liczbami i mogą być używane zamiennie. Odniesienia to także tylko liczby odnoszące się do adresu pamięci o długości 32 lub 64 bitów (chociaż nie można ich używać jako takich - chyba że się z nimi nie mierzyszsun.misc.Unsafe
).boolean
,byte
,short
,char
,int
,long
,float
Idouble
.)Gdzie są przechowywane prymitywne pola?
Pola pierwotne są przechowywane jako część obiektu, który jest gdzieś utworzony . Najłatwiejszym sposobem wymyślenia, gdzie to jest - jest kupa. Jednak nie zawsze tak jest. Jak opisano w teorii i praktyce Java: Legendy o wydajności w mieście, ponownie :
Zatem poza powiedzeniem „obiekt jest tworzony, a pole też tam jest”, nie można powiedzieć, czy coś jest na stosie, czy na stosie. Zauważ, że w przypadku małych, krótkotrwałych obiektów możliwe jest, że „obiekt” nie będzie istniał w pamięci jako taki i może zamiast tego mieć swoje pola bezpośrednio w rejestrach.
Artykuł kończy się na:
Zatem jeśli masz kod, który wygląda następująco:
gdzie
...
nie pozwalaqux
na opuszczenie tego zakresu,qux
może być zamiast tego przydzielone na stosie. Jest to w rzeczywistości wygrana dla maszyny wirtualnej, ponieważ oznacza, że nie trzeba jej nigdy zbierać śmieci - zniknie, gdy opuści zakres.Więcej na temat analizy ucieczki z Wikipedii. Dla tych, którzy chcą zagłębić się w dokumenty, Escape Analysis for Java od IBM. Dla tych, którzy pochodzą ze świata C #, może się okazać, że stos jest szczegółem implementacji, a prawda o typach wartości Erica Lipperta dobrze czyta (są one przydatne w przypadku typów Java, ponieważ wiele koncepcji i aspektów jest takich samych lub podobnych) . Dlaczego książki .Net mówią o alokacji pamięci stosu a sterty? również w to wchodzi.
Na czym polega stos i stos
Na stosie
Dlaczego w ogóle stos lub stos? W przypadku rzeczy, które opuszczają zakres, stos może być kosztowny. Rozważ kod:
Parametry też są częścią stosu. W sytuacji, gdy nie masz stosu, przekazujesz pełny zestaw wartości na stosie. Jest to w porządku
"foo"
i małe ciągi ... ale co by się stało, gdyby ktoś umieścił ogromny plik XML w tym ciągu. Każde wywołanie byłoby skopiować cały ogromny ciąg na stosie - a to byłoby dość rozrzutny.Zamiast tego lepiej jest umieścić obiekty, które mają trochę życia poza bezpośrednim zasięgiem (przekazane do innego zasięgu, utknięte w strukturze, którą ktoś inny utrzymuje itp.) W innym obszarze zwanym stertą.
Na stosie
Nie potrzebujesz stosu. Można hipotetycznie napisać język, który nie używa stosu (o dowolnej głębokości). Tak zrobił stary BASIC, którego nauczyłem się w młodości, można było wykonać tylko 8 poziomów
gosub
wywołań, a wszystkie zmienne były globalne - nie było stosu.Zaletą stosu jest to, że gdy masz zmienną, która istnieje z zakresem, kiedy go opuszczasz, ramka stosu jest wyskakująca. To naprawdę upraszcza to, co jest, a czego nie ma. Program przechodzi do innej procedury, nowej ramki stosu; program powraca do procedury, a ty wróciłeś do tego, który widzi twój obecny zakres; program opuszcza procedurę, a wszystkie elementy na stosie zostają zwolnione.
To naprawdę ułatwia życie osobie piszącej środowisko wykonawcze, aby kod używał stosu i stosu. Po prostu wiele koncepcji i sposobów pracy nad kodem pozwala osobie piszącej kod w języku uwolnić się od bezpośredniego myślenia o nim.
Charakter stosu oznacza również, że nie można go podzielić. Fragmentacja pamięci to prawdziwy problem ze stertą. Przydzielasz kilka obiektów, następnie zbierasz śmieci środkowy, a następnie próbujesz znaleźć miejsce na następny duży do przydzielenia. To bałagan. Umieszczenie rzeczy na stosie zamiast tego oznacza, że nie musisz sobie z tym poradzić.
Kiedy coś jest zbierane śmieci
Kiedy coś jest zbierane, znikają. Ale to tylko śmieci są zbierane, ponieważ już o nich zapomniano - nie ma już żadnych odwołań do obiektu w programie, do których można uzyskać dostęp z bieżącego stanu programu.
Zaznaczę, że jest to bardzo duże uproszczenie zbierania śmieci. Istnieje wiele śmieciarek (nawet w Javie - możesz ulepszyć śmieciarza za pomocą różnych flag ( dokumentów ). Zachowują się one inaczej, a niuanse tego, jak każdy z nich robi rzeczy, są nieco zbyt głębokie na tę odpowiedź. Możesz przeczytać Podstawy Java Garbage Collection, aby lepiej zrozumieć, jak niektóre z nich działają.
To powiedziawszy, jeśli coś jest przydzielane na stosie, nie jest to śmieci gromadzone jako część
System.gc()
- jest ono zwalniane, gdy pojawia się ramka stosu. Jeśli coś jest na stosie i do którego odwołuje się coś na stosie, nie będzie to wtedy śmieci.Dlaczego to ma znaczenie?
W przeważającej części jego tradycja. Podręczniki pisane, klasy kompilatorów i różne bity dokumentują wiele o kupie i stosie.
Jednak dzisiejsze maszyny wirtualne (JVM i podobne) dołożyły wszelkich starań, aby ukryć to przed programistą. Jeśli nie zabraknie jednego lub drugiego i nie musisz wiedzieć, dlaczego (zamiast odpowiednio zwiększać przestrzeń dyskową), nie ma to większego znaczenia.
Obiekt jest gdzieś i znajduje się w miejscu, w którym można uzyskać do niego szybki i prawidłowy dostęp przez odpowiedni czas, jaki istnieje. Jeśli jest na stosie lub na stosie - to naprawdę nie ma znaczenia.
źródło
źródło
Na stercie, chyba że Java przydzieli instancję klasy na stosie jako optymalizację po udowodnieniu za pomocą analizy zmiany znaczenia, że nie wpłynie to na semantykę. Jest to jednak szczegół implementacji, więc dla wszystkich praktycznych celów oprócz mikrooptymalizacji odpowiedź brzmi „na stosie”.
Pamięć stosu musi być przydzielana i zwalniana w kolejności od pierwszego do pierwszego. Pamięć stert można przydzielać i zwalniać w dowolnej kolejności.
Kiedy obiekt jest odśmiecany, nie ma już żadnych odnośników wskazujących na niego ze stosu. Gdyby tak było, utrzymaliby obiekt przy życiu. Prymitywy stosu wcale nie są śmieciami, ponieważ są automatycznie niszczone po powrocie funkcji.
źródło
Pamięć stosu służy do przechowywania zmiennych lokalnych i wywoływania funkcji.
Podczas gdy pamięć sterty służy do przechowywania obiektów w Javie. Bez względu na to, gdzie obiekt jest tworzony w kodzie.
W tym przypadku prymityw a jest powiązany z obiektem klasy A. Tworzy więc w Pamięci Sterty.
Garbage Collector działa w ramach pamięci Heap, więc niszczy obiekty, które nie mają łańcucha referencyjnego od do katalogu głównego.
źródło
Dla lepszego zrozumienia (pamięć sterty / stosu):
źródło