Int 数组用非零元素初始化

根据 JLS,int数组应该在初始化后立即用零填充。然而,我面临的情况并非如此。这种行为首先出现在 JDK7u4中,在以后的所有更新中也会出现(我使用的是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 优化有关。对这种行为的原因有什么想法吗?

更新:
我在 HotSpot 64位服务器 VM 上看到了这种行为,在 Gentoo Linux、 Debian Linux (内核3.0版本)和 MacOS Lion 上 Java 版本从1.7.0 _ 04到1.7.0 _ 10。此错误总是可以与上面的代码一起重现。我没有在32位 JDK 或 Windows 上测试这个问题。我已经向 Oracle 发送了一个 bug 报告(bug id 7196857) ,几天后它将出现在公共 Oracle bug 数据库中。

更新:
Oracle 在他们的公共 bug 数据库 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7196857中发布了这个 bug

7257 次浏览

我修改了你的代码。 这不是整数溢出的问题,看看代码,它在运行时抛出一个异常

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

在这里,我们在 JIT 编译器中遇到了一个 bug。编译器确定分配的数组在 Arrays.fill(...)中分配之后被填充,但是在分配和填充之间的使用检查是错误的。因此,编译器执行非法优化-它跳过了分配的数组归零。

这个 bug 放在 Oracle bug 跟踪器(窃听器编号7196857)中。不幸的是,我没有等到 Oracle 就以下几点作出任何澄清。正如我所看到的,这个 bug 是特定于操作系统的: 它在64位 Linux 和 Mac 上完全可以复制,但是,正如我从评论中看到的,它在 Windows 上不经常复制(对于类似版本的 JDK)。另外,如果能知道这个 bug 什么时候修复就好了。

目前只有一个建议: 如果新声明的数组依赖于 JLS,则不要使用 JDK1.7.0 _ 04或更高版本。

10月5日更新:

在2012年10月4日发布的 JDK 7u10(早期访问)的新 建10个中,这个 bug 至少在 Linux 操作系统中得到了修复(我没有为其他操作系统进行测试)。感谢@Makoto,他发现这个 bug 在 Oracle bug 数据库中不再可用于公共访问。不幸的是,我不知道为什么 Oracle 从公共访问中删除了它,但是它可以在 Google 缓存中使用。此外,这个错误已经引起了红帽子的注意: CVE 标识符 CVE-2012-4420(疯子)和 CVE-2012-4416(疯子)被分配到这个缺陷。