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

2022-01-16 00:00:00 string garbage-collection java

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

In Java, when an object has got no live reference, it is eligible for garbage collection. Now in case of a string, this is not the case because the string will go into the string pool and JVM will keep the object alive for re-use. So that means a string once created will 'never' be garbage collected?

推荐答案

现在对于字符串,情况并非如此,因为字符串将进入字符串池,而 JVM 将保持对象存活以供重用.那么这意味着一旦创建的字符串将永远"不会被垃圾收集?

Now in case of a string, this is not the case because string will go into the string pool and JVM will keep the object alive for reuse. So that means a string once created will 'never' be garbage collected?

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

First, it is only string literals (see notes) that get automatically interned / added to the string pool. String objects that are created by an application at runtime are not interned ... unless your application explicitly calls String.intern().

其次,实际上在字符串池中垃圾收集对象的规则与其他String对象的规则相同:确实是所有对象.如果 GC 发现它们无法访问,它们将被垃圾回收.

Second, in fact the rules for garbage collecting objects in the string pool are the same as for other String objects: indeed all objects. They will be garbage collected if the GC finds them to be unreachable.

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

In practice, the String objects that correspond to string literals typically do not become candidates for garbage collection. This is because there is an implicit reference to the String object in the code of every method that uses the literal. This means that the String is reachable for as long as the method could be executed.

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

However, this is not always the case. If a string literal was defined in a class that was dynamically loaded (e.g. using Class.forName(...)), then it is possible to arrange that the class is unloaded. If that happens, then the String object corresponding to the literal may then be unreachable, and may ultimately be GC'ed.

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

注意事项:

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

  1. A string literal (JLS 3.10.5) is a string that appears in Java source code; e.g.

  "abc"            // string literal
  new String(...)  // not a string literal

  • 通过评估(编译时)常量表达式(JLS 15.28) 也可能被实习.

       "abc" + 123      // this is a constant expression
    

  • 严格来说,并不是所有的 String 字面量都是 inned:

  • Strictly speaking, not all String literals are interned:

    • 如果字符串字面量仅作为常量表达式的子表达式出现在源代码中,则该字面量可能不会出现在.class"中.任何形式的文件.这样的文字不会被实习,因为它在运行时不存在.

    • If a String literal only appears in the source code as a sub-expression of a constant expression, then the literal may not appear in the ".class" file in any form. Such a literal won't be interned because it won't exist at runtime.

    在 Java 9+ 中,涉及非编译时常量的文字和值的字符串连接可能会以不同方式处理.现在,在字节码编译器的选项中,字符串连接如下:

    In Java 9+, string concatenations involving literals and values that are not compile time constants may be handled differently. Now, at the option of the bytecode compiler, a string concatenation like the following:

     int x = 42;   // not a compile time constant
     String s = "prefix " + x + " suffix";
    

    可能会产生一个字符串常量,如下所示:

    may result in a string constant like the following being interned:

     "prefix 1 suffix"
    

    在运行时,上面的字符串常量被用作配方".用于生成动态连接方法.原始字符串文字(即prefix"suffix")不会变成interned string objects.

    At runtime, the above string constant is used as the "recipe" for generating a dynamic concatenation method. The original string literals (i.e. "prefix " and " suffix") would not turn into interned string objects.

    感谢 @Holger 指出这一点.更多细节在 JEP 280 和 StringConcatFactory 的 .com/javase/9​​/docs/api/java/lang/invoke/StringConcatFactory.html" rel="nofollow noreferrer">javadoc.

    Kudos to @Holger for pointing this out. More details are in JEP 280 and the javadoc for StringConcatFactory.

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

    Prior to Java 7, the string pool was in PermGen. For some versions of Java, garbage collection of PermGen was not enabled by default if you selected the CMS collector. But CMS was never the default collector AND there was a flag to enable PermGen collection by CMS. (And nobody should be developing code for Java 6 and earlier anymore.)

  • 相关文章