深入分析String.intern和String常量的實(shí)現(xiàn)原理

轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝!
簡(jiǎn)書(shū)占小狼
http://www.itdecent.cn/users/90ab66c248e6/latest_articles

背景

字符串類(lèi)型在實(shí)際應(yīng)用場(chǎng)景中使用非常頻繁,如果為每個(gè)字符串常量都生成一個(gè)對(duì)應(yīng)的String對(duì)象,明顯會(huì)造成內(nèi)存的浪費(fèi),針對(duì)這一問(wèn)題,虛擬機(jī)實(shí)現(xiàn)一個(gè)字符串常量池的概念,提供了如下實(shí)現(xiàn):
1、同一個(gè)字符串常量,在常量池只有一份副本;
2、通過(guò)雙引號(hào)聲明的字符串,直接保存在常量池中;
3、如果是String對(duì)象,可以通過(guò)String.intern方法,把字符串常量保存到常量池中;

本文JVM源碼版本 openjdk-7-fcs-src-b147-27

疑惑

在不同環(huán)境執(zhí)行上述代碼,會(huì)得到不同的結(jié)果,為什么?
1、JDK1.6的結(jié)果:false false
2、JDK1.7的結(jié)果:true false

解惑

其中String.intern在java中是native方法,JDK1.7的注釋如下:

1、執(zhí)行intern方法時(shí),如果常量池中存在和String對(duì)象相同的字符串,則返回常量池中對(duì)應(yīng)字符串的引用;
2、如果常量池中不存在對(duì)應(yīng)的字符串,則添加該字符串到常量中,并返回字符串引用;

HotSpot1.6實(shí)現(xiàn)

常量池的內(nèi)存在永久代進(jìn)行分配,永久代和Java堆的內(nèi)存是物理隔離的,執(zhí)行intern方法時(shí),如果常量池不存在該字符串,虛擬機(jī)會(huì)在常量池中復(fù)制該字符串,并返回引用,使用intern方法時(shí)需要謹(jǐn)慎,避免常量池中字符串過(guò)多,導(dǎo)致性能變慢,甚至發(fā)生PermGen內(nèi)存溢出。

顯然s.intern() == s不可能成立.

HotSpot1.7實(shí)現(xiàn)

intern方法的HotSpot實(shí)現(xiàn)入口位于openjdk\jdk\src\share\native\java\lang\String.c文件中:

其中JVM_InternString聲明位于openjdk\hotspot\src\share\vm\prims\jvm.cpp文件中:

String.intern最終通過(guò)StringTable.intern方法實(shí)現(xiàn),其中StringTable是HotSpot字符串常量池的具體實(shí)現(xiàn),1.7的常量池已經(jīng)在Java堆上分配內(nèi)存。

常量池的初始化

常量池的實(shí)現(xiàn)非常簡(jiǎn)單,類(lèi)似JDK中的HashMap,其中StringTable的聲明位于symbolTable.hpp文件中:

StringTable最終繼承了BasicHashtable,通過(guò)構(gòu)造方法參數(shù)指定常量池的大小StringTableSize,默認(rèn)為1009,StringTableSize定義在globals.hpp文件中:

不過(guò)在Java7u40版本之后StringTableSize擴(kuò)大到了60013,可以通過(guò)-XX:StringTableSize = 10009設(shè)置StringTable大小,通過(guò)-XX:+PrintFlagsFinal打印虛擬機(jī)的Global flags參數(shù),可以獲得當(dāng)前StringTable的大小。

BasicHashtable實(shí)現(xiàn)

1、initialize方法初始化常量池的基本值:_table_size、_entry_size等;
2、NEW_C_HEAP_ARRAY方法在堆上分配HashtableBucket;
3、清空StringTable中的HashtableBucket數(shù)據(jù);

StringTable.intern實(shí)現(xiàn)

1、其中參數(shù)string_or_null為指向原字符串的句柄,name是String對(duì)象中字符數(shù)組的拷貝、len為字符數(shù)組的長(zhǎng)度;
2、java_lang_String::hash_string方法計(jì)算出字符串的hash值,實(shí)現(xiàn)如下:

3、BasicHashtable.hash_to_index方法計(jì)算出該hash值在StringTable中桶的位置index,實(shí)現(xiàn)如下:

4、StringTable::lookup方法判斷StringTable指定位置的桶中是否存在相等的字符串,實(shí)現(xiàn)如下:

lookup方法通過(guò)遍歷HashtableEntry鏈表,如果找到對(duì)應(yīng)的hash值,且字符串值也相等,說(shuō)明StringTable中已經(jīng)存在該字符串,則返回該字符串引用,否則返回NULL;
5、如果StringTable不存在該字符串,則通過(guò)StringTable::basic_add方法添加字符串引用到StringTable,實(shí)現(xiàn)如下:

basic_add方法中的條件判斷!string_or_null.is_null()為true,!JavaObjectsInPerm為true,所以并不會(huì)進(jìn)行字符串的復(fù)制,而是通過(guò)HashtableEntry對(duì)象封裝原字符串的hash值和指向源字符串的句柄,添加到StringTable對(duì)應(yīng)bucket的鏈表中,并返回指向原字符串句柄;其中變量JavaObjectsInPerm默認(rèn)為false,定義如下:

通過(guò)上述分析:HotSpot1.7實(shí)現(xiàn)的常量池在java堆上分配內(nèi)存,執(zhí)行intern方法時(shí),如果常量池已經(jīng)存在相等的字符串,則直接返回字符串引用,否則復(fù)制該字符串引用到常量池中并返回;

1、對(duì)于變量s1,常量池中不存在"StringTest",所以s1.intern()和 s1都是指向Java堆上的String對(duì)象;
2、對(duì)于變量s2,常量池中一開(kāi)始就已經(jīng)存在"java"字符串,s2.intern()方法返回的是另外一個(gè)"java"字符串對(duì)象,所以s2.intern()和s2指向的并非同一個(gè)對(duì)象;

字符串常量如何實(shí)現(xiàn)?

類(lèi)似String s = "hello java"的字符串常量聲明,在HotSpot中是如何實(shí)現(xiàn)的呢?

其中字符串常量"hello java"會(huì)在編譯過(guò)程中被保存在class文件的Constant pool數(shù)據(jù)結(jié)構(gòu)中,如下是編譯字節(jié)碼實(shí)現(xiàn):

String s = "hello java"對(duì)應(yīng)了兩條字節(jié)碼實(shí)現(xiàn):
1、ldc #2
2、astore_1

其中l(wèi)dc指令的實(shí)現(xiàn)在interpreterRuntime.cpp文件中,實(shí)現(xiàn)如下:

ldc指令中會(huì)根據(jù)獲取的常量類(lèi)型進(jìn)行不同操作,由于目前是字符串常量,從而調(diào)用pool->string_at(index, CHECK)邏輯,實(shí)現(xiàn)如下:

其中h_this是指向當(dāng)前constantPoolOop實(shí)例的句柄,最后調(diào)用string_at_impl方法:

字符串常量一開(kāi)始以Symbol類(lèi)型表示,最終通過(guò)StringTable::intern方法生成字符串對(duì)象,并把字符串的真實(shí)引用更新到constantPool中,這樣下次執(zhí)行l(wèi)dc指令時(shí)可以直接返回對(duì)象引用。


我是占小狼
坐標(biāo)魔都,白天上班族,晚上是知識(shí)的分享者
如果讀完覺(jué)得有收獲的話,歡迎點(diǎn)贊加關(guān)注

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容