什么时候会在Java中对字符串进行垃圾回收


69

在Java中,当对象没有实时引用时,就可以进行垃圾回收。现在,如果是字符串,则不是这种情况,因为该字符串将进入字符串池,并且JVM将使该对象保持活动状态以供重用。因此,这意味着一旦创建了字符串,“永远”不会被垃圾回收?

Answers:


78

现在,如果是字符串,则不是这种情况,因为字符串将进入字符串池,并且JVM将使对象保持活动状态以便重新使用。因此,这意味着一旦创建了字符串,“永远”不会被垃圾回收?

首先,只有字符串文字(请参见注释)会被自动插入/添加到字符串池中。 String应用程序在运行时创建的对象不会被阻止...除非您的应用程序显式调用String.intern()

其次,实际上,在字符串池中垃圾回收对象的规则与其他String对象(实际上是所有对象)相同。如果GC发现它们不可达,将对它们进行垃圾收集。

实际上,String对应于字符串文字的对象通常不会成为垃圾回收的候选对象。这是因为在使用文字的每个方法的代码中都有对对象的隐式引用String。这意味着String只要方法可以执行,就可以访问。

但是,并非总是如此。如果在动态加载的类(例如使用Class.forName(...))中定义了字符串文字,则可以安排该类被卸载。如果发生这种情况,则String对应于文字的对象可能无法访问,并最终被GC处理。

另请参阅:Java何时以及如何收集类垃圾?


笔记:

  1. 字符串文字(JLS 3.10.5)是Java源代码中出现的字符串; 例如

      "abc"            // string literal
      new String(...)  // not a string literal
    
  2. 也可以插入通过评估(编译时)常量表达式(JLS 15.28)而产生的字符串。

       "abc" + 123      // this is a constant expression
    
  3. 严格来说,并不是所有的String文字都被嵌入。如果String文字仅在源代码中作为常量表达式的子表达式出现,则该文字可能不会以任何形式出现在“ .class”文件。这样的文字不会被插入,因为它在运行时将不存在。

  4. 在Java 7之前,字符串池位于PermGen中。对于某些Java版本,如果选择了CMS收集器,则默认情况下不会启用PermGen的垃圾收集。但是CMS从来不是默认的收集器,并且有一个标记来启用CMS的PermGen收集。(而且没有人应该再为Java 6和更早版本开发代码。)


嗨,斯蒂芬,一个问题,当您通过具有静态标签但具有动态值来创建XML有效负载时会发生什么?只有标签会被嵌入,而动态值不会?还是在这种情况下如何避免占用大量内存?谢谢你……
Diego Ramos

1
@DiegoRamos-1)提出一个新问题,提供示例代码来说明您的要求。显示如何创建XML有效负载。2)即使考虑到这一点,您也可能沉迷于过早的优化。3)明确的实习机会几乎没有什么不同。
斯蒂芬C:

8

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

但是,大多数字符串都没有被拘禁。
字符串文字是interned,传递到String.intern()的字符串是intern,但其他所有字符串都不是internated,可以正常地进行GC处理。


2
请记住,“文字字符串”会占用存储空间,因为它们实际上是程序的一部分。他们必须在那里,否则您的程序将丢失一些东西。
2013年

12
“实习生池中的字符串将永远不会被GC处理。” -对于现代的Hotspot JVM,这是不正确的。它更复杂...
Stephen C

6
@Jaskey-您刚刚创建的字符串new将不会被插入。表示字符串文字的String对象被实习。
Stephen C

2
请注意:此答案已过时,不适用于现代JVM。
拉曼·萨哈西

2
@RamanSahasi-它不是过时的。首先,它从来都不是完全正确的。
斯蒂芬C,

2

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

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

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

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

双方s1s2在字符串池中引用的String对象。

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

例如: String s3 = s1 + s2;

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


“字符串池中的字符串对象将不会被垃圾回收。” -这是不正确的。字符串池>> is <<已收集垃圾,并且自(我认为)JDK 1.1起。(在JDK 1.6和更早版本中,如果使用CMS,则需要设置JVM选项以启用PermGen GC。但是CMS从来不是默认的GC。)
Stephen C
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.