我运行了这些代码,我遇到了一些问题,这有点奇怪。
使用字符串:
while(true)
{
String s = String.valueOf(System.currentTimeMillis());
System.out.println(s);
Thread.sleep(10);
}
使用 StringBuilder:
StringBuilder s = null;
while(true)
{
s = new StringBuilder();
s.append(System.currentTimeInMillis());
System.out.println(s);
Thread.sleep(10);
}
在这两种情况下,他们都陷入了 12540 K 的内存浪费。在 Windows XP SP2 上运行此测试。
为什么他们要浪费同样数量的内存?
为什么不可变字符串不再浪费内存?
题外话:如何将 StringBuilder
转换为以特定字符集编码的字节数组?
最佳答案
很难弄清楚您在这里实际问的是什么,但应用程序的行为完全符合我的预期。
Strings are immutable and the garbage collector doesn't take them out. isn't it
可变和不可变对象(immutable对象)都可能在 Java 中被垃圾回收。
确定对象是否真正被垃圾回收的实际标准是可达性。简单来说,当垃圾收集器发现应用程序不能再使用某个对象时,该对象将被删除。
在您的两个应用程序中,每 10 毫秒创建一次大小大致相同的对象。在每次迭代中,都会创建一个新对象并将其引用分配给 s
,以替换先前的引用。这使得之前的对象无法访问,并且有资格进行垃圾回收。在某个时候,Java VM 决定运行垃圾收集器。这摆脱了所有无法访问的对象......并且应用程序继续。
I read that common Strings are not collected ever by the garbage collector, is that false?
这在两个方面是错误的:
由
new String(...)
、String.substring(...)
1等创建的字符串与任何其他 Java 对象没有区别。interned(通过调用
String.intern()
)的字符串存储在 PermGen 堆中的字符串池中2 。然而,即使是 PermGen 堆也会被垃圾回收,尽管时间尺度比正常创建对象的堆更长。
(曾几何时,PermGen 堆没有被垃圾回收,但很久以前就改变了。)
正如@MichaelBorgwardt 正确识别的那样,您将字符串对象(通常)与对应于字符串文字的字符串对象混淆了。后者会自动驻留,并最终进入字符串池。但是,它们可能仍要进行垃圾回收。如果父类被卸载并且没有其他内容引用文字,就会发生这种情况。
1 - 在 Java 6 和更早版本中,使用 new String
和使用 String.substring
创建的字符串是有区别的。在后一种情况下,原始字符串和子字符串将共享保存字符串字符的后备数组。在 Java 7 中,这发生了变化。 String.substring
现在创建一个新的后备数组。
2 - 从 Java 7 开始,字符串池只是普通堆中的一个(隐藏)数据结构。从 Java 8 开始,PermGen 堆不再存在。
关于java - 如果 Java 字符串是不可变的而 StringBuilder 是可变的,为什么它们在我的代码中浪费了相同数量的内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3967646/