String.intern()的垃圾收集行为

Rav*_*pta 23 java string performance garbage-collection

如果我使用String.intern()来提高性能,因为我可以使用"=="来比较实习字符串,我会遇到垃圾收集问题吗?实习字符串的垃圾收集机制与普通字符串有何不同?

Tho*_*nin 21

String.intern()管理一个内部的,本机实现的池,它具有一些特殊的GC相关功能.这是旧代码,但如果它重新实现,它将使用a java.util.WeakHashMap.弱引用是一种保持指向对象的指针而不阻止它被收集的方法.对于统一池,如实习字符串,这是正确的事情.

可以使用以下Java代码演示可以使用垃圾收集的实际字符串:

public class InternedStringsAreCollected {

    public static void main(String[] args)
    {
        for (int i = 0; i < 30; i ++) {
            foo();  
            System.gc();
        }   
    }

    private static void foo()
    {
        char[] tc = new char[10];
        for (int i = 0; i < tc.length; i ++)
            tc[i] = (char)(i * 136757);
        String s = new String(tc).intern();
        System.out.println(System.identityHashCode(s));
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码创建相同字符串的30倍,每次实习.此外,它还用于System.identityHashCode()显示Object.hashCode()在该实习字符串上返回的哈希代码.运行时,此代码打印出不同的整数值,这意味着每次都不会获得相同的实例.

无论如何,使用String.intern()有点气馁.它是一个共享的静态池,这意味着它很容易成为多核系统的瓶颈.使用String.equals()比较字符串,你会活得更长,更快乐.

  • 如果两个线程在两个碰巧具有相同内容的字符串上调用`String.intern()`,那么它们必须都获得相同的引用.这必然意味着两个核心之间的某种通信.实际上,`String.intern()`是用一个受互斥锁保护的排序哈希表来实现的,每次访问(读或写)都会锁定互斥锁.可能存在对该互斥锁的争用,但大多数减速将是由于核心必须同步其L1缓存(这种同步是由互斥锁定所暗示的,并且是昂贵的部分). (2认同)

Rid*_*del 11

实际上,这不是垃圾收集优化,而是字符串池优化.当您调用时String.intern(),将初始String的引用替换为其基本引用(第一次遇到此字符串的引用,或者此引用(如果尚未知)).

但是,一旦您的字符串在应用程序中不再使用,它​​将成为垃圾收集器问题,因为实习字符串池是String类的静态成员,并且永远不会被垃圾回收.

根据经验,我认为从不使用这个实习方法,并且让编译器仅将它用于常量字符串,这些声明如下:

String myString = "a constant that will be interned";
Run Code Online (Sandbox Code Playgroud)

这是更好的,在某种意义上它不会让你做错假设==可以工作,当它不会.

此外,事实是String.equals潜在地==称为优化,确保在引擎盖下使用实习字符串优化.这是一个更多的证据==应该永远被串上使用.

  • '... interned string pool是String类的静态成员'不,不是.String.intern()是一种本机方法.这一切都已经过时了.现在,实习生的字符串已经可以用GC了. (30认同)
  • @Ravi:实习生的JavaDoc(http://java.sun.com/javase/6/docs/api/)说:"所有文字字符串都被实习." (5认同)
  • 哇,谢谢你的整改.当我在1999年开始编程时,Java 1.2是相当新的,与实习生相关的文档非常稀疏.十年后,精神错误得到解决! (3认同)
  • 答案是错误的,因为实习生的字符串被垃圾收集了 (2认同)

Ale*_*der 6

本文提供了完整的答案。

在Java 6中,字符串池位于PermGen中,因为在Java 7中,字符串池位于堆内存中。

手动插入的字符串将被垃圾收集。
仅当卸载定义了字符串文字的类时,才将其作为垃圾文字收集。

字符串池是具有固定大小的HashMap,在Java 6和Java 7的早期版本中较小,但是从Java 7u40开始增加到60013。
可以使用-XX:StringTableSize = <新大小>进行更改,并使用-XX:+ PrintFlagsFinal Java选项进行查看。