在Java中,当一个对象没有实时引用时,它就有资格进行垃圾回收。现在,对于字符串,情况并非如此,因为字符串将进入字符串池,JVM将保持对象的活动状态以供重用。这意味着一个字符串一旦创建就永远不会被垃圾回收?
现在,对于字符串,情况并非如此,因为字符串将进入字符串池,JVM将保持对象的活动状态以供重用。这意味着一个字符串一旦创建就永远不会被垃圾回收?
首先,只有字符串文本(请参阅注释)会自动插入/添加到字符串池中。应用程序在运行时创建的String
对象不是内部的。。。除非您的应用程序显式调用CCD_ 2。
其次,事实上,字符串池中垃圾收集对象的规则与其他String
对象的规则相同:实际上是所有对象。如果GC发现它们不可访问,它们将被垃圾收集。
在实践中,与字符串文字相对应的String
对象通常不会成为垃圾收集的候选者。这是因为在使用文本的每个方法的代码中都有对String
对象的隐式引用。这意味着只要该方法可以执行,String
就可以访问。
然而,情况并非总是如此。如果在动态加载的类中定义了字符串文字(例如使用Class.forName(...)
),则可以安排该类卸载。如果发生这种情况,那么与文字相对应的String
对象可能然后是不可访问的,并且最终可能是GC’ed。
另请参阅: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
-
严格地说,并不是所有的字符串文字都被实习:
-
如果字符串文字仅作为常量表达式的子表达式出现在源代码中,则该文字可能不会出现在"中;。类";任何形式的文件。这样的文字不会被保留,因为它在运行时不存在。
-
在Java 9+中,涉及非编译时常数的文字和值的字符串串联可能会以不同的方式处理。现在,根据字节码编译器的选择,可以进行如下字符串串联:
int x = 42; // not a compile time constant String s = "prefix " + x + " suffix";
可能导致字符串常量,如以下内容:
"prefix 1 suffix"
在运行时,上述字符串常数被用作";配方";用于生成动态级联方法。原始字符串文字(即
"prefix "
和" suffix"
)不会变成内部字符串对象。感谢@Holger指出这一点。更多详细信息请参阅JEP280和
StringConcatFactory
的javadoc。
-
-
在Java7之前,字符串池在PermGen中。对于某些版本的Java,如果您选择CMS收集器,则默认情况下,PermGen的垃圾收集未启用。但CMS从来都不是默认的收集器,而且有一个标志可以启用CMS的PermGen收集。(没有人应该再为Java6和更早版本开发代码了。)
您是正确的;实习生池中的字符串永远不会是GC’d。
然而,大多数字符串上没有实习
字符串文本是中间的,传递给String.intern()
的字符串是中间的。但所有其他字符串都不是中间的,可以正常GC。
字符串池中的字符串对象不会被垃圾回收。如果您在程序执行中没有引用其他String对象,那么它将被垃圾收集。
您可能会询问哪些字符串对象进入字符串池。字符串池中的对象是:
-
编译时文字(例如
String s1 = "123";
) -
运行时中的实习生字符串对象(例如
String s2 = new String("test").intern();
)
s1
和s2
都引用字符串池中的字符串对象。
任何在运行时创建的、未被内部处理的对象都将充当普通对象并驻留在堆内存中。这些对象可以进行垃圾收集。
例如:String s3 = s1 + s2;
这里,s3
引用一个字符串对象,该对象与其他对象(不在字符串池中)一起驻留在堆内存中。
在Java 7之前,字符串池位于永久生成空间中。因此,字符串文字从未被垃圾收集(这也多次导致内存不足问题)在Java7之后,字符串池被放置在堆空间中,堆空间是JVM收集的垃圾。它还减少了JVM中出现内存不足问题的几率。