Java String的intern

String.intern()原理

String.intern()是一個Native方法,底層調用C++的 StringTable::intern 方法,源碼註釋:當調用 intern 方法時,如果常量池中已經該字符串,則返回池中的字符串;否則將此字符串添加到常量池中,並返回字符串的引用。

package com.ctrip.ttd.whywhy;
class Test {
    public static void main(String args[]) {
        String s1 = new StringBuilder().append("String").append("Test").toString();
        System.out.println(s1.intern() == s1);

        String s2 = new StringBuilder().append("ja").append("va").toString();
        System.out.println(s2.intern() == s2);
    }
}

在 JDK6 和 JDK7 中結果不一樣:

1、JDK6的執行結果:false false
對於這個結果很好理解。在JDK6中,常量池在永久代分配內存,永久代和Java堆的內存是物理隔離的,執行intern方法時,如果常量池不存在該字符串,虛擬機會在常量池中複製該字符串,並返回引用,所以需要謹慎使用intern方法,避免常量池中字符串過多,導致性能變慢,甚至發生PermGen內存溢出。
Java  String的intern
2、JDK7的執行結果:true false
對於這個結果就有點懵了。在JDK7中,常量池已經在Java堆上分配內存,執行intern方法時,如果常量池已經存在該字符串,則直接返回字符串引用,否則複製該字符串對象的引用到常量池中並返回,所以在JDK7中,可以重新考慮使用intern方法,減少String對象所佔的內存空間。
Java  String的intern
對於變量s1,常量池中沒有 “StringTest” 字符串,s1.intern() 和 s1都是指向Java對象上的String對象。
對於變量s2,常量池中一開始就已經存在 “java” 字符串,所以 s2.intern() 返回常量池中 “java” 字符串的引用。

String.intern()性能

常量池底層使用StringTable數據結構保存字符串引用,實現和HashMap類似,根據字符串的hashcode定位到對應的數組,遍歷鏈表查找字符串,當字符串比較多時,會降低查詢效率。

在JDK6中,由於常量池在PermGen中,受到內存大小的限制,不建議使用該方法。
在JDK7、8中,可以通過-XX:StringTableSize參數StringTable大小,下面通過幾個測試用例看看intern方法的性能

public class StringTest {
public class StringTest {
    public static void main(String[] args) {
        System.out.println(cost(1000000));
    }

    public static long cost(int num) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < num; i++) {
            String.valueOf(i).intern();
        }
        return System.currentTimeMillis() - start;
    }
}

執行一百萬次intern()方法,不同StringTableSize的耗時情況如下:
1、-XX:StringTableSize=1009, 平均耗時23000ms;
2、-XX:StringTableSize=10009, 平均耗時2200ms;
3、-XX:StringTableSize=100009, 平均耗時200ms;
4、默認情況下,平均耗時400ms;

在默認StringTableSize下,執行不同次intern()方法的耗時情況如下:
1、一萬次,平均耗時5ms;
2、十萬次,平均耗時25ms;
3、五十萬次,平均耗時130ms;
4、一百萬次,平均耗時400ms;
5、五百萬次,平均耗時5000ms;
6、一千萬次,平均耗時15000ms;
從這些測試數據可以看出,儘管在Java 7以上對intern()做了細緻的優化,但其耗時仍然很顯著,如果無限制的使用intern()方法,將導致系統性能下降,不過可以將有限值的字符串放入常量池,提高內存利用率,所以intern()方法是一把雙刃劍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章