"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,但这些永远不会“生存”。