字符串常量池
首先励饵,記錄一點:
字符串池的確切位置沒有被指定在刺,并且可以從一個JVM實現(xiàn)到另一個不同跃闹。
值得注意的是,在Java 7之前磷雇,該池位于熱點JVM上的堆的permgen空間中偿警,但自Java 7以來它已被移至堆的主要部分。而在Java 8 Hotspot中唯笙,Permanent Generation已被徹底刪除螟蒸。
區(qū)域:HotSpot 概要:在JDK 7中盒使,interned字符串不再分配在Java堆的永久生成中,而是分配在Java堆的主要部分(稱為年輕人和老年人)以及其他人由應(yīng)用程序創(chuàng)建的對象七嫌。此更改將導(dǎo)致更多數(shù)據(jù)駐留在主Java堆中少办,永久生成中的數(shù)據(jù)更少,因此可能需要調(diào)整堆大小诵原。由于這種變化英妓,大多數(shù)應(yīng)用程序在堆使用中只會看到相對較小的差異,但是加載很多類或大量使用String.intern()方法的較大應(yīng)用程序?qū)吹礁@著的差異绍赛。RFE:6962931
java.lang.String#intern
然后蔓纠。這個方法在jdk1.6與idk1.7之后發(fā)生了變化。主要是因為jdk1.7之后吗蚌,方法區(qū)中字符串常量池的位置從方法區(qū)變成了堆上腿倚,intern()方法也做了相應(yīng)的修改。
(注:jdk1.8已經(jīng)移除了方法區(qū)褪测,取而代之的是元空間)
我們看下API:
翻譯一下猴誊,String類的intern()方法:一個初始為空的字符串池,它由類String獨自維護侮措。當調(diào)用 intern方法時懈叹,如果池已經(jīng)包含一個等于此String對象的字符串(用equals(oject)方法確定),則返回池中的字符串分扎。否則澄成,將此String對象添加到池中,并返回此String對象的引用畏吓。 對于任意兩個字符串s和t墨状,當且僅當s.equals(t)為true時,s.intern() == t.intern()才為true菲饼。所有字面值字符串和字符串賦值常量表達式都使用 intern方法進行操作肾砂。
例子
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
如果是JDK6- ,那么運行的結(jié)果是false宏悦,false镐确。
如果是JDK7+,運行的結(jié)果是false饼煞,true源葫。
分析
JDK6
先針對jdk1.6來分析。intern()方法在jdk1.6中的工作原理是:
String s = new String("aaa");s.intern();在常量池中尋找常量“aaa”,如果存在砖瞧,則返回這個池中的字符串息堂,如果不存在,將s指向的堆上的對象“aaa”復(fù)制后存在常量池中,并返回池中“aaa”的一個引用(這其實說明了運行時常量池具有動態(tài)性)荣堰。
按照上面的描述開始進行分析床未。
String s = new String("1");
這句話實際創(chuàng)建了兩個對象,一個是常量池中的字符串常量“1’”振坚,另一個是堆上的String對象即硼,s是它的引用。
s.intern();
這一句在池中尋找“1”,可以找到屡拨。所以池中沒有發(fā)生改變。
String s2 = "1";
在池中尋找“1”褥实,可以找到呀狼,所以s2指向池中“1”
所以運行之后s指向堆中對象,s2指向池中對象损离,當然不是指向同一個對象哥艇,結(jié)果為false.
繼續(xù)往下看
String s3 = new String("1") + newString("1");
這句話執(zhí)行之后,s3指向堆上值為“11”的一個對象僻澎,池中有“1”,但是沒有“11”.
s3.intern()貌踏;
在池中尋找“11”,沒有找到窟勃,所以在池中添加了“11”
String s4 = "11"祖乳;
在池中尋找“11”,能夠找到秉氧,所以s4指向了池中的“11”
所以運行后眷昆,s3指向堆中的對象,s4指向池中對象汁咏,結(jié)果為false.
JDK7
再來針對jdk1.7進行分析亚斋。
字符串常量池的位置從方法區(qū)變成了堆上。jdk1.7中intern()工作原理:
String s = new String("aaa");
s.intern();
在常量池中尋找“aaa”,如果已經(jīng)存在攘滩,則返回池中“aaa”這個對象帅刊。如果不存在,那么不會在常量池中復(fù)制一份s指向的對象“aaa”漂问,而是在常量池中記錄了首次出現(xiàn)的對象引用赖瞒。假設(shè)這個引用叫p,p與s指向了堆上同一個對象级解,即p = s冒黑。
根據(jù)上面的描述,開始進行逐句分析勤哗。
String s = new String("1");
這句話實際創(chuàng)建了兩個對象抡爹,一個是常量池中的字符串常量“1’”,另一個是堆上的String對象芒划,s是它的引用冬竟。
s.intern();
這一句在池中尋找“1”,可以找到欧穴。所以池中沒有發(fā)生改變。
String s2 = "1";
在池中尋找“1”泵殴,可以找到涮帘,所以s2指向池中“1”
所以運行之后s指向堆中對象,s2指向池中對象笑诅,當然不是指向同一個對象调缨,結(jié)果為false.
這一部分與jdk1.6版本運行的結(jié)果是一樣的,因為intern()查找的字符串在常量池中都已經(jīng)存在了吆你。
繼續(xù)往下看
String s3 = new String("1") + newString("1");
這句話執(zhí)行之后弦叶,s3指向堆上值為“11”的一個對象,池中有“1”,但是沒有“11”.
s3.intern();
在常量池中尋找“11”妇多,沒有找到伤哺,此時不是在常量池中添加“11”,而是在常量池中添加一個堆上“11”對象的引用者祖,假設(shè)這個引用叫p,p = s3.intern()立莉。p和s3指向的是堆中的同一個對象,p = s3七问。
String s4 = "11".
在常量池中尋找“11”這個對象蜓耻,發(fā)現(xiàn)p指向的對象正是“11”,那么s4也指向了堆上的“11”對象。
那么s3與s4最后都指向了堆上的“11”對象械巡,所以s3 = s4.
思考
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s4 = "11";
String s3 = new String("1") + new String("1");
s3.intern();
System.out.println(s3 == s4);
這個的結(jié)果是什么媒熊,為什么?