Java String.intern()_學習筆記

參考:https://www.jianshu.com/p/0d1c003d2ff5java

String.intern()

String.intern()是native方法,底層調用c++中的StringTable:intern方法。c++

當調用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 中結果不同:app

一、JDK6的執行結果:false false
對於這個結果很好理解。在JDK6中,常量池在永久代分配內存,永久代和Java堆的內存是物理隔離的,執行intern方法時,若是常量池不存在該字符串,虛擬機會在常量池中複製該字符串,並返回引用,因此須要謹慎使用intern方法,避免常量池中字符串過多,致使性能變慢,甚至發生PermGen內存溢出。性能

 
 

二、JDK7的執行結果:true false
對於這個結果就有點懵了。在JDK7中,常量池已經在Java堆上分配內存,執行intern方法時,若是常量池已經存在該字符串,則直接返回字符串引用,不然複製該字符串對象的引用到常量池中並返回,因此在JDK7中,能夠從新考慮使用intern方法,減小String對象所佔的內存空間。測試

 
 

對於變量s1,常量池中沒有 "StringTest" 字符串,s1.intern() 和 s1都是指向Java對象上的String對象。
對於變量s2,常量池中一開始就已經存在 "java" 字符串,因此 s2.intern() 返回常量池中 "java" 字符串的引用。優化

String.intern()性能

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

在JDK6中,因爲常量池在PermGen中,受到內存大小的限制,不建議使用該方法。spa

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的耗時狀況以下:
一、-XX:StringTableSize=1009, 平均耗時23000ms;
二、-XX:StringTableSize=10009, 平均耗時2200ms;
三、-XX:StringTableSize=100009, 平均耗時200ms;
四、默認狀況下,平均耗時400ms;

在默認StringTableSize下,執行不一樣次intern()方法的耗時狀況以下:
一、一萬次,平均耗時5ms;
二、十萬次,平均耗時25ms;
三、五十萬次,平均耗時130ms;
四、一百萬次,平均耗時400ms;
五、五百萬次,平均耗時5000ms;
六、一千萬次,平均耗時15000ms;

從這些測試數據能夠看出,儘管在Java 7以上對intern()作了細緻的優化,但其耗時仍然很顯著,若是無限制的使用intern()方法,將致使系統性能降低,不過能夠將有限值的字符串放入常量池,提升內存利用率,因此intern()方法是一把雙刃劍。

相關文章
相關標籤/搜索