简短版本:如果调用string.substring(n,m).intern(),字符串表是否保留子字符串或原始字符串?
......但我不确定这是一个正确的问题,所以这里是长版本:
我正在使用遗留Java代码(PCGen),它通过将每个文件作为一个大字符串进行篡改来解析文件,然后使用String.split,.trim,.substring和StringTokenizer将它们分解为标记.这对解析非常有效,因为这些方法都不会复制原始字符串,但都指向共享char []的部分.
解析结束后,我想收回一些内存.只需要原始大字符串的几个小子串,但强引用可以防止收集大字符串.后来我遇到了OOM,我相信部分归因于大量解析文件的巨大堆影响.
我知道我可以通过new String(String)
写入时复制大字符串(copy-on-write).我知道我可以通过String.intern减少字符串重复(这很重要,因为在解析的文件中有很多冗余).我是否需要同时使用两者来回收最大量的堆,或者.intern()是否同时执行这两种操作?读取OpenJDK7热点源代码(hotspot/src/share/vm/classfile/symbolTable.cpp),它看起来像字符串表保留整个字符串,并且根本不修剪偏移/长度.所以我想我需要创建一个新的String然后实习结果.对?
所有这一切,切换到流式解析器在内存方面将是一个巨大的胜利,但这对于短期而言是一个太大的变化.
Pet*_*rey 10
您可以使用新的String(String)和intern()方法,这将根据Java 7更新4的需要进行复制.从Java 7更新5子字符串将采用更深的副本,但您可能仍希望使用实习生().注意:Java 7使用堆而不是perm gen来存储String文字.
public static void main(String[] args) {
char[] chars = new char[128];
Arrays.fill(chars, 'A');
String a128 = new String(chars);
printValueFor("a128", a128);
String a16 = a128.substring(0, 16);
printValueFor("a16", a16);
}
public static void printValueFor(String desc, String s) {
try {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] valueArr = (char[]) value.get(s);
System.out.println(desc + ": " + Integer.toHexString(System.identityHashCode(valueArr)) + ", len=" + valueArr.length);
} catch (Exception e) {
throw new AssertionError(e);
}
}
Run Code Online (Sandbox Code Playgroud)
在Java 7更新4打印
a128: 513e86ec, len=128
a16: 53281264, len=16
Run Code Online (Sandbox Code Playgroud)
我希望Java 6不会这样做.