为什么追加“”到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工作或工作的方式避免每次复制一百万个字符。 妥协是妥协的,如果你知道妥协是什么的话,这是很好的。