什么时候在java中对字符串进行垃圾收集
Posted
技术标签:
【中文标题】什么时候在java中对字符串进行垃圾收集【英文标题】:When will a string be garbage collected in java 【发布时间】:2013-08-26 17:36:33 【问题描述】:在 Java 中,当一个对象没有实时引用时,它就有资格进行垃圾回收。现在在字符串的情况下,情况并非如此,因为字符串将进入字符串池,而 JVM 将保持对象处于活动状态以供重用。 那么这意味着一旦创建的字符串将“永远”不会被垃圾收集?
【问题讨论】:
【参考方案1】:现在对于字符串,情况并非如此,因为字符串将进入字符串池,而 JVM 将保持对象存活以供重用。那么这意味着一旦创建的字符串将“永远”不会被垃圾收集?
首先,只有字符串literals(见注释)会被自动插入/添加到字符串池中。 String
应用程序在运行时创建的对象不会被实习......除非您的应用程序显式调用 String.intern()
。
其次,实际上字符串池中垃圾收集对象的规则与其他String
对象的规则相同:确实是所有对象。如果 GC 发现它们无法访问,它们将被垃圾回收。
实际上,对应于字符串文字的String
对象通常不会成为垃圾回收的候选对象。这是因为在每个使用文字的方法的代码中都有一个对String
对象的隐式 引用。这意味着只要可以执行该方法,String
就可以访问。
然而,并非总是如此。如果在动态加载的类中定义了字符串文字(例如使用Class.forName(...)
),则可以安排该类卸载。如果发生这种情况,那么对应于字面量的 String
对象可能将无法访问,最终可能会被 GC 处理。
另请参阅:When and how are classes garbage collected in Java?
注意事项:
字符串文字 (JLS 3.10.5) 是出现在 Java 源代码中的字符串;例如
"abc" // string literal
new String(...) // not a string literal
通过评估(编译时)常量表达式 (JLS 15.28) 生成的字符串也可以被实习。
"abc" + 123 // this is a constant expression
严格来说,并不是所有的 String 字面量都是 inned:
如果字符串文字仅作为常量表达式的子表达式出现在源代码中,那么该文字可能不会以任何形式出现在“.class”文件中。这样的文字不会被实习,因为它在运行时不存在。
在 Java 9+ 中,涉及非编译时常量的文字和值的字符串连接可能会以不同方式处理。现在,在字节码编译器的选项中,字符串连接如下:
int x = 42; // not a compile time constant
String s = "prefix " + x + " suffix";
可能会产生一个字符串常量,如下所示:
"prefix \1 suffix"
在运行时,上述字符串常量用作生成动态连接方法的“配方”。 原始 字符串文字(即"prefix "
和" suffix"
)不会变成内部字符串对象。
感谢@Holger 指出这一点。更多详细信息请参见 JEP 280 和 javadoc for StringConcatFactory
。
在 Java 7 之前,字符串池位于 PermGen 中。对于某些 Java 版本,如果您选择了 CMS 收集器,默认情况下不会启用 PermGen 的垃圾收集。但是 CMS 从来都不是默认的收集器,并且有一个标志可以通过 CMS 启用 PermGen 收集。 (而且没有人应该再为 Java 6 及更早版本开发代码了。)
【讨论】:
【参考方案2】:你是对的;实习生池中的字符串永远不会被 GC。
但是,大多数字符串都没有实习。
字符串 literals 是 interned,传递给 String.intern()
的字符串是 interned,但所有其他字符串不是 interned,可以正常 GC'd。
【讨论】:
请记住,“文字字符串”会占用存储空间,因为它们实际上是程序的一部分。他们必须在那里,否则你的程序会丢失一些东西。 "实习池中的字符串永远不会被 GC'd。" - 这对于现代 Hotspot JVM 是不正确的。它更复杂...... @Jaskey - 您刚刚使用new
创建的字符串不会被保留。表示字符串字面量的 String 对象 将 被实习。
请注意:此答案已过时,不适用于现代 JVM。
@RamanSahasi - 它并没有过时。它从一开始就不是完全正确的。【参考方案3】:
字符串池中的字符串对象不会被垃圾回收。如果您在程序执行中没有引用其他 String 对象,则将被垃圾回收。
您可能会问哪些字符串对象进入字符串池。字符串池中的对象是:
编译时文字(例如String s1 = "123";
)
运行时中的内部字符串对象(例如String s2 = new String("test").intern();
)
s1
和 s2
都引用字符串池中的字符串对象。
在运行时创建的任何对象都将作为普通对象并驻留在堆内存中。这些对象可以被垃圾回收。
这方面的一个例子是:String s3 = s1 + s2;
这里,s3
引用了一个字符串对象,该对象与其他对象(不在字符串池中)一起驻留在堆内存中。
【讨论】:
“字符串池中的字符串对象不会被垃圾回收。” - 这是不正确的。字符串池>>is 【参考方案4】:在 Java 7 之前,字符串池驻留在永久代空间中。所以字符串文字永远不会被垃圾收集(这也导致了很多次内存不足的问题) Java 7 之后,字符串池被放置在堆空间中,这是 JVM 收集的垃圾。它还减少了在 JVM 中出现内存不足问题的机会。
【讨论】:
这个答案有很多错误的陈述。字符串 literals 曾经(并且现在)不太可能被垃圾收集的原因是声明它们的类通常在应用程序的整个生命周期内都可以访问。无论字符串池是在 permgen 还是常规堆中,这都是正确的。 (permgen >被以上是关于什么时候在java中对字符串进行垃圾收集的主要内容,如果未能解决你的问题,请参考以下文章