Java: масив int ініціалізується з ненульовими елементами


130

Згідно JLS, intмасив повинен бути заповнений нулями відразу після ініціалізації. Однак я зіткнувся з ситуацією, коли це не так. Така поведінка виникає спочатку в JDK 7u4, а також відбувається у всіх пізніших оновленнях (я використовую 64-бітну реалізацію). Наступний код кидає виняток:

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);
    }

Виняток відбувається після того, як JVM виконує компіляцію блоку коду і не виникає з -Xintпрапором. Крім того, Arrays.fill(...)заява (як і всі інші твердження в цьому коді) є необхідною, і виняток не відбувається, якщо він відсутній. Зрозуміло, що ця можлива помилка обмежена певною оптимізацією JVM. Якісь ідеї з причини такої поведінки?

Оновлення:
я бачу цю поведінку на 64-розрядному сервері VM HotSpot, версії Java від 1.7.0_04 до 1.7.0_10 на Gentoo Linux, Debian Linux (обидві версії ядра 3.0) та MacOS Lion. Ця помилка завжди може бути відтворена за допомогою коду вище. Я не тестував цю проблему з 32-розрядним JDK або в Windows. Я вже надіслав звіт про помилку до Oracle (помилка id 7196857), і він з’явиться у відкритій базі даних про помилки Oracle через кілька днів.

Оновлення:
Oracle опублікував цю помилку у своїй публічній базі помилок: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7196857


15
Я б сказав, що помилка в реалізації, якщо вона не дотримується специфікації
Petesh

12
Оскільки у вас є чітко визначений приклад, який надійно відтворює проблему (принаймні, на деяких платформах), ви обдумали подати помилку ?
Йоахім Зауер

4
Так, вам, безумовно, слід подати звіт про помилку. Це дуже серйозна помилка!
Гарячі лизання

7
Так, я вже надіслав звіт про помилку до Oracle (помилка id 7196857), і він з’явиться у відкритій базі даних про помилки Oracle через кілька днів.
Станіслав Пославський

6
Я спробував це з оновленням Java 7, 64-бітне в Windows, і у нього не було проблем.
Пітер Лорі

Відповіді:


42

Тут ми стикаємося з помилкою в JIT-компіляторі. Компілятор визначає, що виділений масив заповнюється після розподілу Arrays.fill(...), але перевірка на використання між розподілом та заливкою є помилковою. Отже, компілятор виконує незаконну оптимізацію - пропускає нулінг виділеного масиву.

Ця помилка розміщена в програмі помилок Oracle ( id помилки 7196857 ). На жаль, я не дочекався якихось роз'яснень від Oracle щодо наступних моментів. Як я бачу, ця помилка стосується ОС: вона абсолютно відтворювана на 64-бітних Linux та Mac, але, як я бачу з коментарів, вона відтворюється не регулярно в Windows (для подібних версій JDK). Крім того, було б непогано знати, коли ця помилка буде виправлена.

Наразі є лише порада: не використовуйте JDK1.7.0_04 або пізнішої версії, якщо ви залежите від JLS для нещодавно оголошених масивів.

Оновлення 5 жовтня:

У новій збірці 10 JDK 7u10 (раннього доступу), опублікованій 04 жовтня 2012 року, ця помилка була виправлена ​​принаймні для ОС Linux (для інших я не тестувала). Дякуємо @Makoto, який виявив, що ця помилка більше не доступна для загального доступу в базі даних помилок Oracle. На жаль, я не знаю, з яких причин Oracle видалив його з загальнодоступного доступу, але він доступний у кеші Google . Також ця помилка привернула увагу Redhat: до цього недоліку були призначені ідентифікатори CVE CVE-2012-4420 ( bugzilla ) та CVE-2012-4416 ( bugzilla ).


2
Ідентифікатор помилки зараз недійсний - ви можете це вивчити?
Макото

1
@Makoto Я розгублений, оскільки ця помилка була в базі даних про помилки вчора. Я не знаю, з якої причини Oracle видалив цю помилку із загальнодоступного доступу. Але Google пам’ятає webcache.googleusercontent.com/… Крім того, ця помилка була розміщена також у базі даних про помилки RedHat, оскільки вона може призвести до CVE bugzilla.redhat.com/show_bug.cgi?id=856124
Станіслав Пославський

0

Я внесла деякі зміни у ваш код. Це не проблема переповнення Integer. Дивіться код, він викидає виняток під час виконання

    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++;
    }

Windows 7 64 біт. Jdk 64 біт 1.7.0_07
Роберто Мерегетті
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.