Java OutOfMemoryError dziwne zachowanie
Zakładając, że mamy pamięć max 256M, dlaczego ten kod działa:
public static void main(String... args) {
for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
Ale ten rzuca OOME?
public static void main(String... args) {
//for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
2 answers
Aby zachować perspektywę, rozważ użycie tego kodu z -Xmx64m
:
static long sum;
public static void main(String[] args) {
System.out.println("Warming up...");
for (int i = 0; i < 100_000; i++) test(1);
System.out.println("Main call");
test(5_500_000);
System.out.println("Sum: " + sum);
}
static void test(int size) {
// for (int i = 0; i < 1; i++)
{
long[] a2 = new long[size];
sum += a2.length;
}
long[] a1 = new long[size];
sum += a1.length;
}
W zależności od tego, czy wykonasz rozgrzewkę, czy pominiesz ją, będzie dmuchać,czy nie. Dzieje się tak dlatego, że poprawny kod JITted null
jest poza var, podczas gdy interpretowany kod nie. oba zachowania są akceptowalne zgodnie ze specyfikacją języka Java, co oznacza, że jesteś z tym na łasce JVM.
Testowane z Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
na OS X.
Bytecode analiza
Spójrz na bajt kodu za pomocą pętli for
(prosty kod, bez zmiennej sum
):
static void test(int);
Code:
0: iconst_0
1: istore_1
2: goto 12
5: iload_0
6: newarray long
8: astore_2
9: iinc 1, 1
12: iload_1
13: iconst_1
14: if_icmplt 5
17: iload_0
18: newarray long
20: astore_1
21: return
I bez:
static void test(int);
Code:
0: iload_0
1: newarray long
3: astore_1
4: iload_0
5: newarray long
7: astore_1
8: return
Brak jawnego null
w obu przypadkach, ale zauważ, że w przykładzie no-for ta sama lokalizacja pamięci jest rzeczywiście ponownie używana, w przeciwieństwie do przykładu for. Prowadziłoby to, jeśli już, do oczekiwania przeciwnego do obserwowanego zachowania.
A twist...
Na podstawie tego, czego dowiedzieliśmy się z bytecode, spróbuj uruchomić to:
public static void main(String[] args) {
{
long[] a1 = new long[5_000_000];
}
long[] a2 = new long[0];
long[] a3 = new long[5_000_000];
}
No OOME thrown . Skomentuj deklarację a2
i już jest. Przydzielamy więcej , ale zajmujemy mniej ? Spójrz na kod bajtowy:
public static void main(java.lang.String[]);
Code:
0: ldc #16 // int 5000000
2: istore_1
3: ldc #16 // int 5000000
5: newarray long
7: astore_2
8: iconst_0
9: newarray long
11: astore_2
12: ldc #16 // int 5000000
14: newarray long
16: astore_3
17: return
Miejsce 2, używane dla a1
, jest ponownie używane dla a2
. To samo dotyczy kodu OP, ale teraz nadpisujemy lokalizację z odniesieniem do nieszkodliwej tablicy o zerowej długości i używamy innej lokalizacji do przechowywania odniesienia do naszej ogromnej tablicy.
Podsumowując w górę...
Specyfikacja języka Java nie określa, że każdy obiekt śmieci musi być pobrany, a specyfikacja JVM mówi tylko, że "ramka" ze zmiennymi lokalnymi jest niszczona jako całość po zakończeniu metody. Dlatego wszystkie zachowania, których byliśmy świadkami, są zgodne z książką. Stan obiektu invisible (wspomniany w dokumencie linked to by keppil ) jest tylko sposobem na opisanie tego, co dzieje się w niektórych implementacjach i pod niektórymi okoliczności, ale nie jest w żaden sposób kanonicznym zachowaniem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-30 16:34:28
Dzieje się tak dlatego, że podczas gdy a1
nie znajduje się w zakresie po nawiasach, jest w stanie o nazwie invisible dopóki metoda nie powróci.
Większość nowoczesnych systemów JVM nie ustawia zmiennej a1
na null
, gdy tylko opuści zakres (w rzeczywistości to, czy wewnętrzne nawiasy są tam, czy nie, nawet nie zmienia generowanego kodu bajtowego), ponieważ jest to bardzo nieefektywne i zwykle nie ma znaczenia. W związku z tym, a1
nie można zbierać śmieci, dopóki metoda nie powróci.
Możesz sprawdzić to przez dodanie linii
a1 = null;
Wewnątrz nawiasów, co sprawia, że program działa dobrze.
Termin niewidzialny i Wyjaśnienie pochodzi z tego starego papieru: http://192.9.162.55/docs/books/performance/1st_edition/html/JPAppGC.fm.html
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 04:59:03