ChatGPT解决这个技术问题 Extra ChatGPT

什么时候在java中对字符串进行垃圾收集

在 Java 中,当一个对象没有实时引用时,它就有资格进行垃圾回收。现在在字符串的情况下,情况并非如此,因为字符串将进入字符串池,而 JVM 将保持对象处于活动状态以供重用。所以这意味着一旦创建的字符串将“永远”不会被垃圾收集?


S
Stephen C

现在如果是字符串,情况并非如此,因为字符串将进入字符串池,而 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 及更早版本开发代码了。)


S
SLaks

你是对的;实习生池中的字符串永远不会被 GC'd。

但是,大多数字符串都不是 interned。
String literals 是 interned,传递给 String.intern() 的字符串是 interned,但所有其他字符串不是 interned,可以正常进行 GC。


请记住,“文字字符串”占用存储空间,因为它们实际上是您程序的一部分。他们必须在那里,否则你的程序会丢失一些东西。
“实习生池中的字符串永远不会被 GC 处理。” - 这对于现代 Hotspot JVM 是不正确的。它更复杂...
@Jaskey - 您刚刚使用 new 创建的字符串不会被实习。表示字符串文字 的 String 对象将被实习。
请注意:此答案已过时,不适用于现代 JVM。
@RamanSahasi - 它并没有过时。首先,它从来都不是完全正确的。
D
Das_Geek

字符串池中的字符串对象不会被垃圾回收。如果您在程序执行中没有引用它,其他 String 对象将被垃圾收集。

您可能会问哪些字符串对象进入字符串池。字符串池中的对象是:

编译时文字(例如String s1 = "123";)

运行时内的 String 对象(例如 String s2 = new String("test").intern();)

s1s2 都引用字符串池中的字符串对象。

任何在运行时创建且未实习的对象都将充当普通对象并驻留在堆内存中。这些对象可以被垃圾回收。

例如:String s3 = s1 + s2;

这里,s3 引用了一个字符串对象,该对象与其他对象(不在字符串池中)一起驻留在堆内存中。


“字符串池中的字符串对象不会被垃圾回收。” - 这是不正确的。字符串池>>is<< 垃圾收集,并且从(我认为)JDK 1.1 开始。 (在 JDK 1.6 和更早的版本中,如果您使用 CMS,则需要设置 JVM 选项以启用 PermGen GC。但 CMS 从来都不是默认的 GC。)
H
Himani Agarwal

在 Java 7 之前,字符串池位于永久代空间中。所以字符串文字永远不会被垃圾回收(这也导致了很多次内存不足的问题)Java 7 之后,字符串池被放置在堆空间中,这是由 JVM 进行垃圾回收的。它还减少了在 JVM 中出现内存不足问题的机会。


这个答案有许多错误陈述。字符串文字曾经(并且现在)不太可能被垃圾收集的原因是声明它们的类通常在应用程序的整个生命周期内都可以访问。无论字符串池是在 permgen 还是常规堆中,这都是正确的。 (permgen >被<垃圾收集。)