我之前发布了博客文章“使用JDK 7和JDK 8读取慢速行”,并且在该问题上有一些有用的评论来描述该问题。 这篇文章提供了更多解释,说明为何该文章中演示的文件读取(并由Ant的LineContainsRegExp使用 )在Java 7和Java 8中比在Java 6中这么慢。
X Wang的帖子JDK 6和JDK 7中的substring()方法描述了如何在JDK 6和JDK 7之间更改String.substring() 。Wang在该帖子中写道,JDK 6 substring()
“创建了一个新字符串,但字符串的值仍指向堆中相同的[backing char]数组。” 他与JDK 7方法形成对比,“在JDK 7中,substring()方法实际上在堆中创建了一个新数组。”
Wang的文章对于理解Java 6和Java 7之间String.substring()
的区别非常有用。这篇文章的评论也很有见地。 这些评论包括我很感激的观点 ,“我会说'不同'而不是'改善'。” 也有关于JDK 7如何避免 JDK 6中可能发生的潜在内存泄漏的解释 。
StackOverflow线程Java 7字符串–子字符串的复杂性解释了更改的动机,并引用了JDK-4513622错误:(str)保留字段的子字符串会阻止对象的GC 。 该错误指出:“ [发生OutOfMemory错误,因为如果调用者在对象中存储字段的子字符串,则对象不会被垃圾回收。” 该错误包含演示此错误发生的示例代码。 我在这里修改了该代码:
/*** Minimally adapted from Bug JDK-4513622.** {@link http://bugs.java.com/view_bug.do?bug_id=4513622}*/
public class TestGC
{private String largeString = new String(new byte[100000]);private String getString(){return this.largeString.substring(0,2);}public static void main(String[] args){java.util.ArrayList<String> list = new java.util.ArrayList<String>();for (int i = 0; i < 1000000; i++){final TestGC gc = new TestGC();list.add(gc.getString());}}
}
下一个屏幕快照展示了用Java 6(jdk1.6是可执行Java启动程序路径的一部分)和Java 8(主机上的默认版本)执行的最后一个代码段(从Bug JDK-4513622改编而成)。 如屏幕快照所示,在Java 6中运行代码时抛出OutOfMemoryError ,而在Java 8中运行时不抛出OutOfMemoryError 。
换句话说,当对冗长的Java字符串执行String.substring
时,Java 7中的更改修复了潜在的内存泄漏,但以性能影响为代价。 这意味着使用String.substring
(包括Ant的LineContainsRegExp)来处理很长的行的任何实现都可能需要更改以不同的方式实现,或者在从Java 6迁移到Java 7及更高版本时处理很长的行时应避免使用。
一旦知道了问题(在这种情况下更改String.substring
实现),就可以更轻松地在线查找有关正在发生的事情的文档(感谢提供了使这些资源易于查找的注释)。 JDK-4513622的重复错误包含提供额外详细信息的内容。 这些错误是JDK-4637640:由于String.substring()实现而导致的内存泄漏和JDK-6294060:使用substring()导致了内存泄漏 。 其他相关的在线资源包括Java 7中对String.substring的更改 (其中包括对String.intern()的引用-有更好的方法 ), Java 6与Java 7:当实现很重要时 ,以及受到高度评价的(超过350条注释) Reddit线程TIL Oracle更改了Java 7 Update 6中的内部String表示,从而将子字符串方法的运行时间从常量更改为N。
用Java 1.7.0_06进行的更改为String内部表示的文章很好地回顾了此更改,并总结了原始问题,修复程序以及与该修复程序相关的新问题:
现在您可以忘记上面描述的内存泄漏,并且永远不再使用新的String(String)构造函数。 作为缺点,您现在必须记住String.substring现在具有线性复杂度,而不是恒定的复杂度。
翻译自: https://www.javacodegeeks.com/2015/01/reason-for-slower-reading-of-large-lines-in-jdk-7-and-jdk-8.html