Java: int tablica inicjuje się elementami niezerowymi

130

Zgodnie z JLS intzaraz 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 -Xintflagą. 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

Stanislav Poslavsky
źródło
15
Powiedziałbym błąd w implementacji, jeśli nie jest
zgodny
12
Skoro masz dobrze zdefiniowany przykład, który niezawodnie odtwarza problem (przynajmniej na niektórych platformach), czy rozważałeś zgłoszenie błędu ?
Joachim Sauer
4
Tak, zdecydowanie powinieneś zgłosić błąd. To bardzo poważny błąd!
Hot Licks,
7
Tak, 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.
Stanislav Poslavsky,
6
Wypróbowałem to z 64-bitową wersją Java 7 update 7 w systemie Windows i nie było problemu.
Peter Lawrey,

Odpowiedzi:

42

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.

Stanislav Poslavsky
źródło
2
Identyfikator błędu jest teraz nieprawidłowy - czy mógłbyś się temu przyjrzeć?
Makoto,
1
@Makoto Jestem zdezorientowany, ponieważ ten błąd był wczoraj w bazie danych błędów. Nie wiem, dlaczego Oracle usunęło ten błąd z publicznego dostępu. Ale Google pamięta webcache.googleusercontent.com/… Dodatkowo ten błąd został również umieszczony w bazie błędów RedHat, ponieważ może prowadzić do błędu CVE bugzilla.redhat.com/show_bug.cgi?id=856124
Stanislav Poslavsky
0

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++;
    }
Roberto Mereghetti
źródło
Windows 7 w wersji 64-bitowej Jdk 64 bit 1.7.0_07
Roberto Mereghetti