1

我编写了一个示例 java 应用程序,它分配内存然后永远运行。
为什么幸存者空间使用的内存是0kbytes?!

    List<String> stringlist = new ArrayList<String>();

    while (true) {
        stringlist.add("test");
        if (stringlist.size() >= 5000000)
            break;
    }

    while (true)
        for (String s : stringlist);
4

2 回答 2

7

因为“test”是一个字符串文字,它最终会在永久内存而不是堆中。您创建的对象的内存大小为 5000000 + 4*2 ~ 5MB,可以轻松放入 Eden 空间。

调整

stringlist.add("test");

stringlist.add(new String("test"));

你会得到 5000000 * 4 * 2 =38MB,它很可能仍然适合 Eden。您可以增加列表大小或字符串长度以确保您有幸存者。

于 2012-07-20T17:07:49.513 回答
3

"test"是一个String字面量,不管它是如何存储的(这在 Java 开发过程中发生了变化),这里的重点是,它是一个单一的对象

回忆一下 Java 语言规范

…字符串字面量总是指向一个 class 实例String。这是因为字符串文字 - 或者更一般地说,作为常量表达式值的字符串(第 15.28 节) - 是“内部的”,以便使用 String.intern 方法共享唯一实例

String因此,在您的循环中没有创建 new ,因为它"test"总是引用同一个String实例。唯一的堆变化发生在ArrayList的内部容量耗尽时。


的内部数组最终所需的内存ArrayList取决于对象引用的大小,通常5000000*4 bytes用于具有压缩 oops 的 32 位 JVM 和 64 位 JVM 以及5000000*8 bytes没有压缩 oops 的 64 位 JVM。

这里有趣的一点在www.kdgregory.com上有描述:

如果你的对象足够大,它会直接在tenured generation中创建。用户定义的对象不会(不应该!)有任何接近触发此行为所需的成员数量,但数组将:使用 JDK 1.5,大于 0.5 兆字节的数组直接进入终身代。

这与oracle.com 上的这些词一致:

如果幸存者空间太小,复制集合会直接溢出到终身代。

这给出了更大的数组可能不会出现在幸存者空间中的另一个原因。因此,它们是否因为从 Eden 空间复制到 Tenured Generation 或最初在 Tenured Generation 中创建而没有出现取决于确切的配置。但是没有出现在幸存者空间的结果是一样的。


因此,当ArrayList使用其默认容量10创建 时,内部数组小于此阈值,每次容量扩大时要创建的下一个数组也是如此。但是,当新数组超过这个阈值时,所有旧数组都是垃圾,因此不会显示为“幸存者”。

因此,在第一个循环结束时,您只有一个剩余数组,其大小远远超过阈值,因此绕过了幸存者空间。您的第二个循环不会向内存管理添加任何内容。它会创建临时Iterator的 s,但这些永远不会“生存”。

于 2014-08-11T13:23:25.123 回答