Java: массив int инициализируется ненулевыми элементами

В соответствии с 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 за несколько дней.

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

Ответ 1

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

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

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

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

В новой Build 10 JDK 7u10 (ранний доступ), выпущенной 04 октября 2012 года, эта ошибка была исправлена, по крайней мере, для ОС Linux (я не сделал тест для других). Благодаря @Makoto, который обнаружил, что эта ошибка больше не доступна для общего доступа в базе данных Oracle Database. К сожалению, я не знаю, по каким причинам Oracle удалил его из общего доступа, но он доступен в Google cache. Кроме того, эта ошибка привлекла внимание Redhat: идентификаторы CVE CVE-2012-4420 (bugzilla) и CVE-2012-4416 (bugzilla) были присвоены этому недостатку.

Ответ 2

Я сделал некоторые изменения в вашем коде. Это не проблема переполнения 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++;
    }