Czy tablica prymitywów Java jest przechowywana na stosie lub stercie?


85

Mam taką deklarację tablicy:

int a[];

Oto atablica inttypu pierwotnego . Gdzie jest przechowywana ta tablica? Czy jest przechowywany na stercie czy na stosie? Jest to typ intpierwotny, wszystkie typy pierwotne nie są przechowywane na stercie.


38
To nie jest tablica. Jest to odniesienie do tablicy. Samo odwołanie może być przechowywane na stercie, jeśli jest członkiem klasy lub obiektu, lub na stosie, jeśli jest zmienną lokalną w metodzie. Typy pierwotne mogą być przechowywane na stercie, jeśli są członkami klasy lub obiektu.
wujek O

Odpowiedzi:


143

Jak powiedział gurukulki, jest przechowywany na stercie. Jednak Twój post sugerował nieporozumienie, prawdopodobnie spowodowane przez jakąś osobę o dobrych intencjach, która propaguje mit, że „prymitywni zawsze żyją na stosie”. To nieprawda. Zmienne lokalne mają swoje wartości na stosie, ale nie wszystkie zmienne pierwotne są lokalne ...

Na przykład rozważ to:

public class Foo
{
    int value;
}
...

public void someOtherMethod()
{
    Foo f = new Foo();
    ...
}

Gdzie teraz f.valuemieszka? Mit sugerowałby, że jest na stosie - ale w rzeczywistości jest częścią nowego Fooobiektu i żyje na stercie 1 . (Zauważ, że wartość fsama w sobie jest odniesieniem i żyje na stosie).

Stamtąd łatwo jest przejść do tablic. Możesz myśleć o tablicy jako o dużej liczbie zmiennych - tak więc new int[3]jest trochę tak, jakbyś miał klasę w tej postaci:

public class ArrayInt3
{
    public readonly int length = 3;
    public int value0;
    public int value1;
    public int value2;
}

1 W rzeczywistości jest to bardziej skomplikowane niż to. Rozróżnienie między stosem a stertą polega głównie na szczegółach implementacji - uważam, że niektóre maszyny JVM, być może eksperymentalne, potrafią określić, kiedy obiekt nigdy nie „ucieka” z metody i mogą przydzielić cały obiekt na stosie. Jednak koncepcyjnie jest na kupie, jeśli zdecydujesz się na to.


1
O „analizie ucieczki” w Javie: blog.juma.me.uk/2008/12/17/objects-with-no-allocation-overhead Mówi się, że jest obecna od wczesnego wydania JDK 6 Update 14, oraz domyślnie włączone od 23 aktualizacji JDK 6.
Guido

czy to coś zmienia, jeśli tablica jest publiczną statyczną ostateczną? Czy zatem nie powinno to być częścią stałej puli?
Malachiasz

@Malachiasz: Nie. Tablica nigdy nie jest stałą.
Jon Skeet

@JonSkeet: We wszystkich znanych mi wersjach biblioteki Java, każda Stringjest wspierana przez rozszerzenie char[]. Uważam, że dosłowne ciągi znaków są przechowywane w publicznej stałej puli; w przypadku optymalizacji GC oznaczałoby to, że tablice zapasowe powinny być przechowywane w podobny sposób (w przeciwnym razie stałaby pula musiałaby być skanowana podczas dowolnego cyklu GC, gdzie w przeciwnym razie tablica podstawowa kwalifikowałaby się do gromadzenia).
supercat

@supercat: Tak, to miałoby sens. Ale każda tablica zadeklarować się nigdy nie kończy się w ramach puli stałej.
Jon Skeet

37

Będzie przechowywany na stercie

ponieważ tablica jest obiektem w java.

EDYCJA : jeśli masz

int [] testScores; 
testScores = new int[4];

Pomyśl o tym kodzie tak, jakby mówiło kompilatorowi: „Utwórz obiekt tablicy, który będzie zawierał cztery liczby całkowite, i przypisz go do zmiennej referencyjnej o nazwie testScores. Ponadto, ustaw każdy intelement na zero. Dzięki”.


2
A zmienna odniesienia o nazwie testScores (wskazująca na tablicę na stercie) będzie na stosie.
Zaki

11
Kod mówi „Dzięki” tylko wtedy, gdy podasz -gopcję kompilatorowi. W przeciwnym razie zostanie zoptymalizowany.
mob

1
@mob Zakładasz, że to jedyny kod. Prawdopodobnie lepiej jest założyć, że te dwie linie są częścią większego programu, który faktycznie używa tablicy.
Code-Apprentice

22

Jest to tablica typów pierwotnych, która sama w sobie nie jest prymitywna. Dobrą zasadą jest to, że gdy użyte jest nowe słowo kluczowe, wynik będzie na stercie.


18

Chciałem tylko podzielić się kilkoma testami, które przeprowadziłem na ten temat.

Tablica o rozmiarze 10 milionów

public static void main(String[] args) {
    memInfo();
    double a[] = new double[10000000];
    memInfo();
}

Wynik:

------------------------
max mem = 130.0 MB
total mem = 85.0 MB
free mem = 83.6 MB
used mem = 1.4 MB
------------------------
------------------------
max mem = 130.0 MB
total mem = 130.0 MB
free mem = 48.9 MB
used mem = 81.1 MB
------------------------

Jak widać, używany rozmiar stosu został zwiększony o ~ 80 MB, co daje 10m * sizeof (double).

Ale jeśli użyjemy Double zamiast double

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    memInfo();
}

Wyjście pokaże 40 MB. Mamy tylko podwójne referencje, nie są one zainicjalizowane.

Wypełnianie go Double

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];      
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq;
    }
    memInfo();
}

Nadal 40 MB. Ponieważ wszystkie wskazują na ten sam obiekt Double.

Zamiast tego inicjalizuję double

public static void main(String[] args) {
    memInfo();
    Double a[] = new Double[10000000];
    Double qq = 3.1d;
    for (int i = 0; i < a.length; i++) {
        a[i] = qq.doubleValue();
    }
    memInfo();
}

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

Linia

a[i] = qq.doubleValue();

jest równa

a[i] = Double.valueOf(qq.doubleValue());

co jest równoważne

a[i] = new Double(qq.doubleValue());

Ponieważ za każdym razem tworzymy nowe podwójne obiekty, wysadzamy stos. To pokazuje, że wartości wewnątrz klasy Double są przechowywane w stercie.


1
Czy możesz wkleić szczegóły kodu memInfo () pls? :)
hedleyyan

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.