2016-02-12 13 views
5

To pytanie może być głupie lub może być duplikatem. Nie rozumiem, w jaki sposób zmienne są pobierane ze stosu, gdy program odwołuje się do tej zmiennej. Obiekt jest przechowywany w stercie, a jego położenie jest przechowywane w zmiennej referencyjnej, a zmienna referencyjna zawierająca sam adres sterty jest przechowywana w stosie. Ale jak JVM odkrywa, że ​​zmienna referencyjna jest przechowywana w jakiej lokalizacji w stosie.W jaki sposób JVM zna lokalizację zmiennej w stosie metody?

Przyjrzyjmy się przykładowi, aby wyjaśnić, o co mi chodzi.

Class Test { 
    public void test() { 
     Object a = new Bar(); 
     Object b = new Foo(); 
     System.out.println(a); 
    } 
} 

Powiedzmy, że metoda test() jest wykonywana. Tak więc stos zostanie przydzielony do testu().

Teraz, gdy wiersz "Obiekt a = nowy pasek(); 'zostanie wykonany, obiekt Bar zostanie utworzony w Heap, a rzeczywista zmienna "a", której wartość jest lokalizacją adresu obiektu Bar, będzie przechowywana w stosie testu().

Znów na linii "Obiekt b = nowy Foo(); "To samo się dzieje. Obiekt Foo zostanie utworzony w Heap, a rzeczywista zmienna 'b', której wartością jest lokalizacja adresu obiektu Foo, będzie przechowywana w stosie testu().

Teraz, gdy linia "System.out.println (a); 'jest wykonywany, w jaki sposób JVM wie, z której lokalizacji w stosie, należy pobrać wartość "a". Oznacza, co łączy zmienną "a" i jej położenie w stosie?

Odpowiedz

6

Jesteś prawie na miejscu, w twoim zrozumieniu jest tylko jeden brakujący link.

Zmienne lokalne (lub odniesienia do obiektu zapisanego w zmiennej lokalnej, jeśli mówimy o typach niepochodzących) są w rzeczywistości przechowywane w tabeli zmiennych lokalnych , a nie na stosie argumentów. Są one popychane tylko na stos, gdy mają być użyte przez połączenie.

(Mylące jest to, że sama tablica zmiennych lokalnych jest również przechowywana na stosie, ale jest to osobny stos od tego, co kod bajtowy używa dla operandów. Z perspektywy kodu bajtowego jest to prawdziwy stół ze stałym rozmiarem i dowolnie indeksowalny.)

Możesz użyć javap, aby sprawdzić, jaki kod bajtowy jest generowany z twojego kodu. To, co zobaczysz, jest takie:

public void test(); 
descriptor:()V 
flags: ACC_PUBLIC 
Code: 
    stack=3, locals=3, args_size=1 
    0: new   #2     // class Test$Bar 
    3: dup 
    4: invokespecial #3     // Method Test$Bar."<init>":()V 
    7: astore_1 
    8: new   #4     // class Test$Foo 
    11: dup 
    12: invokespecial #5     // Method Test$Foo."<init>":()V 
    15: astore_2 
    16: getstatic  #6     // Field java/lang/System.out:Ljava/io/PrintStream; 
    19: aload_1 
    20: invokevirtual #7     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    23: return 
} 

Po pierwsze, jaka jest ta linia?

stack=3, locals=3, args_size=1 

Jest metadane, które opowiada JVM, że metoda ta ma stosu argumentu nie głębsze niż 3 wpisy, 3 zmiennych lokalnych i trwa 1 argument. Ale na pewno to nie jest w porządku, nasza metoda nie ma żadnych argumentów i wyraźnie ma tylko dwie zmienne lokalne!

Odpowiedź jest taka, że ​​metody niestatyczne mają zawsze "0 argument": this. To tłumaczy liczbę argumentów i prowadzi nas do kolejnego ważnego odkrycia: argumenty metody są przechowywane w tabeli zmiennych lokalnych również.Zatem nasza tabela będzie zawierała pozycje 0, 2, z 0 zawierającą this na początku i 1 i 2 niezainicjowanymi.

Po zejściu z drogi spójrzmy na kod! Najpierw to linie 0-7:

  1. new opcode tworzy nową instancję Bar i przechowuje referencję na stosie.
  2. dup tworzy kopię tego samego odniesienia na szczycie stosu (tak, masz dwie kopie teraz siedzi tam)
  3. invokespecial #3 wywołuje konstruktor Bar i zużywa wierzch stosu. (Teraz mamy tylko jedną kopię lewej)
  4. astore_1 sklepów pozostałe odniesienia w lokalny numer zmiennej liczbie 1 (0 jest this w tym przypadku)

to co Object a = new Bar(); został opracowany w. Otrzymasz to samo dla Object b = new Foo(); (linie 8-15).

A potem przychodzi ciekawy kawałek, z linii 16:

  1. getstatiC#6 pcha wartość System.out na stosie
  2. aload_1 popycha lokalny numer zmienna 1 (a) na stosie za
  3. invokevirtual #7 zużywa oba wpisy, wywołując println() na System.out z a jako jego parametr wejściowy.

Jeśli chcesz zagłębić się w nią głębiej, lub po prostu chcesz podkreślić moje błędy, oficjalne odniesienie do wszystkich wyżej wymienionych jest here.

+0

Dzięki za szczegółową odpowiedź. Po prostu wyjaśnienie: astore_x popchnie do pozycji "x" w tabeli zmiennych lokalnych, a aload_x popchnie pozycję przechowywaną na pozycji "x" w tabeli do stosu. To jest ogólny pomysł rt? – Dinkan

+0

@Dinkan Dokładnie, i oczywiście "astore_x" również wyskakuje ze stosu. – biziclop

+0

OK. Rozumiem. Dzięki. – Dinkan

0

JVM przechowuje ramki stosów, a te utrzymują tablice dla zmiennych.

Each frame (§2.6) contains an array of variables known as its local variables. 
[...] 
Local variables are addressed by indexing. 

Znaleziony Here

0

JVM nie jest pojedynczy struktura danych, w rzeczywistości ma kilka różnych mechanizmów. Po uruchomieniu programu JVM organizuje wszystkie potrzebne pamięci i przydziela je do kilku różnych stosów pamięci o nazwach obszarów danych runtime.

Oto bardziej szczegółowe wyjaśnienie: Architecture of JVM

Powiązane problemy