Zgodnie z JLS int
zaraz po inicjalizacji tablica powinna zostać wypełniona zerami. Jednak mam do czynienia z sytuacją, w której tak nie jest. Takie zachowanie pojawia się najpierw w JDK 7u4, a także we wszystkich późniejszych aktualizacjach (używam implementacji 64-bitowej). Poniższy kod zgłasza wyjątek:
public static void main(String[] args) {
int[] a;
int n = 0;
for (int i = 0; i < 100000000; ++i) {
a = new int[10];
for (int f : a)
if (f != 0)
throw new RuntimeException("Array just after allocation: "+ Arrays.toString(a));
Arrays.fill(a, 0);
for (int j = 0; j < a.length; ++j)
a[j] = (n - j)*i;
for (int f : a)
n += f;
}
System.out.println(n);
}
Wyjątek występuje, gdy maszyna JVM wykonuje kompilację bloku kodu i nie pojawia się z -Xint
flagą. Ponadto Arrays.fill(...)
instrukcja (jak wszystkie inne instrukcje w tym kodzie) jest konieczna, a wyjątek nie występuje, jeśli jej nie ma. Oczywiste jest, że ten możliwy błąd jest związany z pewną optymalizacją JVM. Jakieś pomysły na powód takiego zachowania?
Aktualizacja:
Widzę to zachowanie na 64-bitowej maszynie wirtualnej serwera HotSpot, wersji Java od 1.7.0_04 do 1.7.0_10 w Gentoo Linux, Debian Linux (obie wersje jądra 3.0) i MacOS Lion. Ten błąd można zawsze odtworzyć za pomocą powyższego kodu. Nie testowałem tego problemu z 32-bitowym JDK ani w systemie Windows. Wysłałem już raport o błędzie do Oracle (identyfikator błędu 7196857) i pojawi się on w publicznej bazie błędów Oracle za kilka dni.
Aktualizacja:
Oracle opublikowało ten błąd w swojej publicznej bazie danych błędów: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7196857
Odpowiedzi:
Tutaj mamy do czynienia z błędem w kompilatorze JIT. Kompilator ustala, że przydzielona tablica jest wypełniana po alokacji
Arrays.fill(...)
, ale sprawdzenie użycia między alokacją a wypełnieniem jest błędne. Zatem kompilator przeprowadza niedozwoloną optymalizację - pomija zerowanie przydzielonej tablicy.Ten błąd jest umieszczony w module śledzenia błędów Oracle ( identyfikator błędu 7196857 ). Niestety nie czekałem na jakiekolwiek wyjaśnienia Oracle dotyczące poniższych punktów. Jak widzę, ten błąd jest specyficzny dla systemu operacyjnego: jest całkowicie odtwarzalny w 64-bitowym Linuksie i Macu, ale jak widzę z komentarzy, nie odtwarza się regularnie w systemie Windows (dla podobnych wersji JDK). Poza tym dobrze byłoby wiedzieć, kiedy ten błąd zostanie naprawiony.
W tej chwili jest tylko rada: nie używaj JDK1.7.0_04 lub nowszego, jeśli polegasz na JLS dla nowo zadeklarowanych tablic.
Aktualizacja 5 października:
W nowej kompilacji 10 JDK 7u10 (wczesny dostęp) wydanej 4 października 2012 r. Ten błąd został naprawiony przynajmniej dla systemu operacyjnego Linux (nie testowałem dla innych). Podziękowania dla @Makoto, który stwierdził, że ten błąd nie jest już dostępny do publicznego dostępu w bazie danych Oracle. Niestety nie znam powodów, dla których Oracle usunęło go z publicznego dostępu, ale jest on dostępny w pamięci podręcznej Google . Ponadto ten błąd zwrócił uwagę Redhat: identyfikatory CVE CVE-2012-4420 ( bugzilla ) i CVE-2012-4416 ( bugzilla ) zostały przypisane do tej luki.
źródło
Dokonałem pewnej zmiany w twoim kodzie. To nie jest problem przepełnienia liczby całkowitej. Zobacz kod, zgłasza wyjątek w czasie wykonywania
int[] a; int n = 0; for (int i = 0; i < 100000000; ++i) { a = new int[10]; for (int f : a) { if (f != 0) { throw new RuntimeException("Array just after allocation: " + Arrays.toString(a)); } } for (int ii = 0, len = a.length; ii < len; ii++) a[ii] = 0; for (int j = 0; j < a.length; ++j) a[j] = Integer.MAX_VALUE - 1; for (int j = 0; j < a.length; ++j) n++; }
źródło