为什么追加“”到string保存内存?

我用了一个有很多数据的variables,比如String data 。 我想用下面的方式使用这个string的一小部分:

 this.smallpart = data.substring(12,18); 

经过几个小时的debugging(使用内存可视化工具)后,我发现objects field smallpart记住了所有来自data ,虽然它只包含子string。

当我把代码改成:

 this.smallpart = data.substring(12,18)+""; 

问题解决了! 现在我的应用程序现在使用很less的内存!

这怎么可能? 任何人都可以解释吗? 我认为this.smallpart一直参考数据,但为什么呢?

更新:我怎样才能清除大string呢? 将数据=新的string(data.substring(0,100))做的事情?

执行以下操作:

 data.substring(x, y) + "" 

创build一个新的(较小的)String对象,并抛出对由substring()创build的String的引用,从而启用垃圾回收。

要实现的重要的事情是substring()给一个现有的string一个窗口 – 或者说,原来的string底层的字符数组。 因此它将消耗与原始string相同的内存。 这在某些情况下可能是有利的,但是如果你想得到一个子string并且处理原始的string(就像你已经发现的那样),这是有问题的。

查看JDKstring源中的substring()方法以获取更多信息。

编辑:要回答您的补充问题,从子string构造一个新的string将减less您的内存消耗, 只要您bin任何引用原始string。

注(2013年1月)。 上述行为在Java 7u6中已经改变。 flyweight模式不再使用, substring()将按照您的预期工作。

如果你看看substring(int, int) ,你会看到它返回:

 new String(offset + beginIndex, endIndex - beginIndex, value); 

其中value是原始的char[] 。 所以你得到一个新的string,但具有相同的底层char[]

当你这样做, data.substring() + "" ,你得到一个新的string与一个新的基础char[]

实际上,你的用例是你应该使用String(String)构造函数的唯一情况:

 String tiny = new String(huge.substring(12,18)); 

当你使用substring ,它实际上并没有创build一个新的string。 它仍然是指你的原始string,偏移量和大小的限制。

所以,为了让你的原始string被收集,你需要创build一个新的string(使用new String ,或者你有什么)。

我认为this.smallpart一直参考数据,但为什么呢?

因为Javastring由一个char数组,一个起始偏移量和一个长度(以及一个caching的hashCode)组成。 像substring()这样的一些String操作将创build一个新的String对象,该对象共享原始的char数组,并且只是具有不同的偏移量和/或长度字段。 这是可行的,因为string的char数组一旦创build就永远不会被修改。

当多个子string引用相同的基本string而不复制重叠部分时,这可以节省内存。 正如您已经注意到的,在某些情况下,它可以保留垃圾收集时不再需要的数据。

修正这个问题的“正确”方法是new String(String)构造函数,即

 this.smallpart = new String(data.substring(12,18)); 

顺便说一句,总体上最好的解决scheme将是避免首先有非常大的string,并处理任何input更小的块,一次几KB。

在Java中,string是不可变的对象,一旦创build了一个string,它就一直保留在内存中,直到被垃圾收集器清理掉(并且这种清理不是你理所当然的东西)。

在调用substring方法时,Java不会创build一个trully新string,而只是在原始string中存储一系列字符。

所以,当你用这个代码创build一个新的string:

 this.smallpart = data.substring(12, 18) + ""; 

当你用空string连接结果时,你实际上创build了一个新的string。 这就是为什么。

正如1997年jwz所记载:

如果你有一个巨大的string,取出它的一个substring(),坚持到子string,并允许较长的string变成垃圾(换句话说,子string有一个更长的生命周期)大string的基本字节永远不会远。

总结一下,如果你从less量的大string中创build大量的子string,那就使用

  String subtring = string.substring(5,23) 

既然你只用空间来存放大弦,但是如果你从大弦的丢失中提取了一小部分小弦,那么

  String substring = new String(string.substring(5,23)); 

将保持你的内存使用下来,因为大string可以不再需要时回收。

你调用new String是一个有用的提醒,你真的得到一个新的string,而不是对原来的一个引用。

首先, 调用java.lang.String.substring在原始String上使用偏移量和长度创build新窗口, 而不是复制基础数组的重要部分。

如果我们仔细看substring方法,我们会注意到一个string构造函数调用String(int, int, char[])并将其传递给整个char[]来表示string 。 这意味着子string将占用与原始string一样多的内存量。

好吧,但为什么+ ""导致需求比没有它的内存less?

strings上执行+是通过StringBuilder.append方法调用实现的。 在AbstractStringBuilder类中查看这个方法的实现会告诉我们,它最终会用我们真正需要的部分( substring )来做arraycopy

任何其他的解决方法?

 this.smallpart = new String(data.substring(12,18)); this.smallpart = data.substring(12,18).intern(); 

将“”附加到string有时会节省内存。

比方说,我有一个巨大的string,包含一本全书,一百万字。

然后,我创build了20个string,包含本书的章节作为子string。

然后我创build包含所有段落的1000个string。

然后我创build了包含所有语句的10,000个string

然后我创build了包含所有单词的100,000个string。

我仍然只使用100万个字符。 如果为每个章节,段落,句子和单词添加“”,则使用5,000,000个字符。

当然,如果你只从整本书中提取一个单词,那么整本书就可能被垃圾收集,但这并不是因为这个词有一个引用。

如果你有一个一百万字符的string,并删除两端的标签和空格,说10个调用来创build一个子string,这又是不同的。 Java工作或工作的方式避免每次复制一百万个字符。 妥协是妥协的,如果你知道妥协是什么的话,这是很好的。