我编写了一个示例java应用程序,它分配内存然后永远运行 为什么幸存者空间使用的内存为0kbytes?!
List<String> stringlist = new ArrayList<String>();
while (true) {
stringlist.add("test");
if (stringlist.size() >= 5000000)
break;
}
while (true)
for (String s : stringlist);
答案 0 :(得分:7)
因为“test”是一个字符串文字,所以它最终将永久存储在堆中。 您创建的对象的内存大小为5000000 + 4 * 2~5MB,可轻松融入Eden空间。
修改
stringlist.add("test");
到
stringlist.add(new String("test"));
你将获得5000000 * 4 * 2 = 38MB,这很可能仍然适合伊甸园。您可以增加列表大小或字符串长度,以确保您有幸存者。
答案 1 :(得分:3)
"test"
是一个String
字面值,无论它是如何存储的(这在Java开发期间已经改变),重要的一点是,它是一个单个对象
Recall the Java Language Specification:
...字符串文字始终引用类
String
的相同实例。这是因为字符串文字 - 或者更常见的是作为常量表达式(第15.28节)的值的字符串 - 被“实现”以便使用String.intern方法共享唯一实例
因此,您的循环中没有创建新的String
,因为"test"
始终引用相同的String
实例。当ArrayList
的内部容量耗尽时,唯一的堆发生变化。
ArrayList
内部数组最终需要的内存取决于对象引用的大小,通常是32位JVM的5000000*4 bytes
和压缩oops和5000000*8 bytes
的64位JVM 64位没有压缩oops的JVM。
这里有趣的一点是www.kdgregory.com:
如果您的对象足够大,它将直接在终身代中创建。用户定义的对象不会(不应该!)接近触发此行为所需的成员数量,但数组将:使用JDK 1.5,大于半兆字节的数组将直接进入终结代。
这与these words found on oracle.com:
相协调如果幸存者空间太小,则复制集合会直接溢出到终身代。
这给出了为什么较大的数组可能不会出现在幸存者空间中的另一个原因。因此,它取决于它们是否出现的确切配置,因为它们是从Eden空间复制到Tenured Generation或者首先在Tenured Generation中创建的。但是没有出现在幸存者空间的结果是一样的。
因此,当使用default capacity of 10
创建ArrayList
时,内部数组小于此阈值,因此要在每个容量放大上创建下一个数组。但是,当新阵列超过此阈值时,所有旧阵列都是垃圾,因此不会显示为“幸存者”。
因此,在第一个循环结束时,您只有一个剩余的数组,其大小超过了阈值,因此绕过了幸存者空间。您的第二个循环不会向内存管理添加任何内容。它会创建临时Iterator
,但这些永远不会“存活”。