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-битной серверной виртуальной машине HotSpot, версии Java с 1.7.0_04 до 1.7.0_10 в Gentoo Linux, Debian Linux (обе версии ядра 3.0) и MacOS Lion. Эту ошибку всегда можно воспроизвести с помощью приведенного выше кода. Я не тестировал эту проблему с 32-битным JDK или в Windows. Я уже отправил отчет об ошибке в Oracle (идентификатор ошибки 7196857), и через несколько дней он появится в общедоступной базе данных ошибок Oracle.

Обновление:
Oracle опубликовала эту ошибку в своей общедоступной базе данных ошибок: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7196857

Станислав Пославский
источник
15
Я бы сказал, что это ошибка в реализации, если она не соответствует спецификации
Питеш
12
Поскольку у вас есть четко определенный пример, который надежно воспроизводит проблему (по крайней мере, на некоторых платформах), рассматривали ли вы возможность регистрации ошибки ?
Joachim Sauer
4
Да, вам обязательно нужно отправить отчет об ошибке. Это очень серьезная ошибка!
Hot Licks
7
Да, я уже отправил отчет об ошибке в Oracle (идентификатор ошибки 7196857), и через несколько дней он появится в общедоступной базе данных ошибок Oracle.
Станислав Пославский
6
Я попробовал это с Java 7 update 7 64-bit в Windows, и у меня не было проблем.
Питер Лоури

Ответы:

42

Здесь мы столкнулись с ошибкой в ​​JIT-компиляторе. Компилятор определяет, что выделенный массив заполнен после выделения Arrays.fill(...), но проверка использования между выделением и заполнением ошибочна. Итак, компилятор выполняет недопустимую оптимизацию - пропускает обнуление выделенного массива.

Эта ошибка помещена в трекер ошибок Oracle ( идентификатор ошибки 7196857 ). К сожалению, я не дождался разъяснений от Oracle по следующим пунктам. Как я вижу, эта ошибка специфична для ОС: она абсолютно воспроизводима на 64-битных Linux и Mac, но, как я вижу из комментариев, воспроизводится не регулярно в Windows (для аналогичных версий JDK). Кроме того, было бы неплохо узнать, когда эта ошибка будет исправлена.

На данный момент есть только совет: не используйте JDK1.7.0_04 или более позднюю версию, если вы зависите от JLS для вновь объявленных массивов.

Обновление от 5 октября:

В новой сборке 10 JDK 7u10 (ранний доступ), выпущенной 4 октября 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

Я внес некоторые изменения в ваш код. Это не проблема целочисленного переполнения. Посмотрите код, он выдает исключение во время выполнения

    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 bit 1.7.0_07
Роберто Мерегетти