2010-03-12 102 views
19

如果我使用中的String.intern()来提高性能,因为我可以用“==”来比较实习字符串,将我碰上垃圾收集问题? interned字符串的垃圾收集机制与普通字符串有什么不同?的String.intern垃圾收集行为()

+0

这个问题可能是相关http://stackoverflow.com/questions/372547 – 2010-03-12 09:23:53

+0

参见https://stackoverflow.com/questions/18152560/garbage-collection-on-internd-strings-string-pool-and- perm-space – Vadzim 2017-11-11 11:50:51

回答

9

其实,这不是一个垃圾收集优化,而是一个字符串池优化。 当你调用String.intern(),你与它的基本参考(遇到这个字符串的第一次引用或参考这个,如果它还不知道)代替参考您的初始字符串。

然而,它会成为一个垃圾收集问题,一旦你的字符串是在应用不再使用,因为实习串池是String类的静态成员,永远不会被垃圾收集。

作为一个经验法则,我认为preferrable到从未使用这种实习方式,让编译器仅使用它的常量字符串,这些声明如下:

String myString = "a constant that will be interned"; 

这是更好的,在意识到它不会让你做假设==可以工作时,它不会。

此外,事实上,String.equals潜在地调用==作为优化,使其确实interned字符串优化是在引擎盖下使用。这是一个更多的证据==应该从未在字符串中使用。

+0

我不明白,为什么你的示例中的myString会被拦截?也许如果我们将其标记为最终,那么它将被实施。 – 2010-03-12 09:37:29

+4

@Ravi:实习生的JavaDoc(http://java.sun.com/javase/6/docs/api/)说:“所有文字字符串都是实习生。” – 2010-03-12 09:40:11

+0

+1表示“不要使用==表示字符串”。这可能会发展成一个坏习惯。 – Thilo 2010-03-12 10:00:34

18

String.intern()管理内部,本地实现的游泳池,里面有一些特殊的GC-相关的功能。这是旧的代码,但如果它被重新实现,它也使用java.util.WeakHashMap。弱引用是一种保持指向对象的指针而不阻止它被收集的方法。对于一个统一的池(如interned字符串)来说,这是正确的。

那实习的字符串是收集可以用下面的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)); 
    } 
} 

此代码创建相同的字符串30次,每次实习吧。同时,它采用System.identityHashCode()显示什么哈希码Object.hashCode()将会对实习返回的字符串。运行时,此代码打印出不同的整数值,这意味着每次都不会获得相同的实例。

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

+0

请问为什么这会变成多核系统的瓶颈或提到一个指针? – Sergio 2013-12-28 22:24:51

+1

如果两个线程在恰好具有相同内容的两个字符串上调用'String.intern()',则它们都必须获得相同的引用。这必然意味着两个核心之间的某种沟通。实际上,'String.intern()'是通过一个由互斥锁保护的散列表实现的,每个访问(读或写)都会锁定互斥锁。这个互斥体可能存在争用,但是大部分的放缓都是由于核心必须同步它们的L1缓存(这种同步被互斥锁定所隐含,并且是昂贵的部分)。 – 2013-12-29 14:00:04

+0

为什么实习表不能只是一个ConcurrentHashMap? – user833771 2017-03-29 19:54:16

0

请阅读:http://satukubik.com/2009/01/06/java-tips-memory-optimization-for-string/

我可以从你的信息中得到的结论是:你实习太多字符串。如果你真的需要实习生这么多的字符串进行性能优化,增加烫发根内存,但如果我是你,我将检查第一,如果我真的需要这么多的实习字符串。

+0

@ nanda博客条目的正确链接似乎是:http://blog.firdau.si/2009/01/06/java-tips-memory-optimization-for-string/ – 2011-09-30 21:59:39

2

This article提供了完整的答案。

在java 6中,字符串池驻留在PermGen中,因为java 7字符串池驻留在堆内存中。

手动截取的字符串将被垃圾收集。
如果定义它们的类被卸载,则字符串文字将仅被垃圾收集。

字符串池是一个固定大小的HashMap,它在java 6和早期版本的java 7中很小,但自java 7u40以来增加到60013。
可以使用更改-XX:StringTableSize = <新大小>并使用查看-XX:+ PrintFlagsFinal java选项。