在 Java 中,当一个对象没有实时引用时,它就有资格进行垃圾回收。现在在字符串的情况下,情况并非如此,因为字符串将进入字符串池,而 JVM 将保持对象处于活动状态以供重用。所以这意味着一旦创建的字符串将“永远”不会被垃圾收集?
现在如果是字符串,情况并非如此,因为字符串将进入字符串池,而 JVM 将保持对象处于活动状态以供重用。所以这意味着一旦创建的字符串将“永远”不会被垃圾收集?
首先,它是 only 字符串 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" // 字符串文字 new String(...) // 不是字符串文字 通过(编译时)常量表达式 (JLS 15.28) 的求值产生的字符串也可以被实习。 "abc" + 123 // 这是一个常量表达式 严格来说,并不是所有的String字面量都是interned:如果一个String字面量只作为常量表达式的子表达式出现在源代码中,那么这个字面量可能不会出现在任何形式的“.class”文件。这样的文字不会被实习,因为它在运行时不存在。在 Java 9+ 中,涉及非编译时常量的文字和值的字符串连接可能会有不同的处理方式。现在,在字节码编译器的选项下,字符串连接如下: int x = 42; // 不是编译时常量 String s = "prefix" + x + "suffix";可能会导致一个字符串常量,如下所示: "prefix \1 suffix" 在运行时,上述字符串常量用作生成动态连接方法的“配方”。原始字符串文字(即“前缀”和“后缀”)不会变成内部字符串对象。感谢@Holger 指出这一点。更多细节在 JEP 280 和 StringConcatFactory 的 javadoc 中。在 Java 7 之前,字符串池位于 PermGen 中。对于某些 Java 版本,如果您选择了 CMS 收集器,则默认情况下不会启用 PermGen 的垃圾收集。但是 CMS 从来都不是默认的收集器,并且有一个标志可以通过 CMS 启用 PermGen 收集。 (而且没有人应该再为 Java 6 及更早版本开发代码了。)
你是对的;实习生池中的字符串永远不会被 GC'd。
但是,大多数字符串都不是 interned。
String literals 是 interned,传递给 String.intern()
的字符串是 interned,但所有其他字符串不是 interned,可以正常进行 GC。
字符串池中的字符串对象不会被垃圾回收。如果您在程序执行中没有引用它,其他 String 对象将被垃圾收集。
您可能会问哪些字符串对象进入字符串池。字符串池中的对象是:
编译时文字(例如String s1 = "123";)
运行时内的 String 对象(例如 String s2 = new String("test").intern();)
s1
和 s2
都引用字符串池中的字符串对象。
任何在运行时创建且未实习的对象都将充当普通对象并驻留在堆内存中。这些对象可以被垃圾回收。
例如:String s3 = s1 + s2;
这里,s3
引用了一个字符串对象,该对象与其他对象(不在字符串池中)一起驻留在堆内存中。
在 Java 7 之前,字符串池位于永久代空间中。所以字符串文字永远不会被垃圾回收(这也导致了很多次内存不足的问题)Java 7 之后,字符串池被放置在堆空间中,这是由 JVM 进行垃圾回收的。它还减少了在 JVM 中出现内存不足问题的机会。
不定期副业成功案例分享
new
创建的字符串不会被实习。表示字符串文字 的 String 对象将被实习。