如果Java字符串是不可变的StringBuilder是可变的,为什么他们浪费在我的代码的内存一样多吗?

fre*_*crs 5 java string immutability

我运行了那些代码,我得到了一些问题,这有点奇怪.

使用字符串:

while(true)
{
    String s = String.valueOf(System.currentTimeMillis());
    System.out.println(s);
    Thread.sleep(10);
}
Run Code Online (Sandbox Code Playgroud)

使用StringBuilder:

StringBuilder s = null;
    while(true)
    {
        s = new StringBuilder();
        s.append(System.currentTimeInMillis());
        System.out.println(s);
        Thread.sleep(10);
    }
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,他们都会陷入12540 K的内存浪费.在Windows XP SP2上运行此测试.

他们为什么浪费相同数量的记忆?为什么不可变的String会停止浪费内存?偏离主题:如何转换StringBuilder为特定字符集中编码的字节数组?

Ste*_*n C 9

很难弄清楚你在这里问的是什么,但应用程序的行为与我期望的完全一样.

字符串是不可变的,垃圾收集器不会将它们取出.不是吗

可变和不可变对象都可以用Java进行垃圾收集.

确定对象是否实际上是垃圾收集的实际标准是可达性.简单来说,当垃圾收集器发现应用程序不能再使用对象时,该对象将被删除.

在两个应用程序中,每10毫秒创建一次大小相同的对象.在每次迭代中,正在创建一个新对象并将其引用分配给s,替换先前的引用.这使得先前的对象无法访问,并且有资格进行垃圾回收.在某些时候,Java VM决定运行垃圾收集器.这摆脱了所有无法访问的对象......应用程序继续.

我读过垃圾收集器没有收集过常见的字符串,这是假的吗?

这有两个方面是假的:

  • 通过创建字符串new String(...),String.substring(...)1等没有从任何其他Java对象不同.

  • 拦截的字符串(通过调用String.intern())存储在字符串池中,该字符串池保存在PermGen堆2中.但是,即使是PermGen堆也是垃圾收集的,尽管通常在比通常创建对象的堆的时间更长的时候.

(曾几何时,PermGen堆不是垃圾收集的,但很久以前就已经改变了.)

正如@MichaelBorgwardt正确识别的那样,您将字符串对象(通常)与对应于字符串文字的字符串对象混淆.后者自动实现,最终进入字符串池.但是,他们可能仍然会收集垃圾.如果卸载父类并且没有其他任何内容引用文字,则会发生这种情况.


1 - 在Java 6及更早版本中,使用new String和使用创建的字符串之间存在差异String.substring.在后一种情况下,原始字符串和子字符串将共享保存字符串字符的后备数组.在Java 7中,这发生了变化. String.substring现在创建一个新的后备阵列.

2 - 从Java 7开始,字符串池只是普通堆中的(隐藏)数据结构.从Java 8开始,PermGen堆不再存在.


Mic*_*rdt 6

你混淆了两件非常不同的东西:

  • 字符串是不可变的,但这与它们是否是垃圾收集无关.但是,这意味着如果您需要对String进行大量更改(例如通过一次附加一个字符来构建一个大字符串),那么您最终会为垃圾收集器制作大量副本和大量工作.
  • 字符串文字(即直接在源代码中编写的字符串)是实习字符串池的一部分,通常不会进行垃圾回收.但是,这样做是为了允许源代码中相同String的多个实例被对同一对象的引用替换,这可以节省大量空间.这是唯一可能的,因为字符串是不可变的,因此程序的两个部分保持对同一个字符串的引用不会相互干扰.