<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文介紹Java的String的intern方法的原理。
在 JAVA 語言中有8種基本型別和一種比較特殊的型別String。這些型別為了使他們在執行過程中速度更快,更節省記憶體,都提供了一種常數池(在方法區)的概念。常數池就類似一個JAVA系統級別提供的快取。8種基本型別的常數池都是系統協調的,String型別的常數池比較特殊。
String的常數池的主要使用方法有兩種:
直接使用雙引號宣告出來的String物件會直接儲存在常數池中。
如果不是用雙引號宣告的String物件,可以使用String提供的intern方法將其放到常數池。
原型:public native String intern();
說明:
從字串常數池中查詢當前字串是否存在(通過equals判斷)。
返回值:都是返回String變數對應的字串常數池的參照。
範例
package com.example; public class Demo { public static void main(String argv[]) { String s = "test"; System.out.println(s == s.intern()); } }
JDK6及之前:輸出false
JDK7及之後:輸出true
常數池裡的字串的由來
JDK6及以前呼叫String.intern()
JDK7及以後呼叫String.intern()
例程1:
package org.example.a; public class Demo { public static void main(String argv[]) { String s1 = new String("1"); s1.intern(); String s2 = "1"; System.out.println(s1 == s2); String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4); } }
結果
jdk6:false false
jdk7:false true
jdk8:false true
例程2:
package org.example.a; public class Demo { public static void main(String argv[]) { String s1 = new String("1"); s1.intern(); String s2 = "1"; System.out.println(s1 == s2); String s3 = new String("1") + new String("1"); String s4 = "11"; s3.intern(); System.out.println(s3 == s4); } }
上述程式碼第二部分有個對調。
結果
jdk6:false false
jdk7:false false
jdk8:false false
下邊圖中:綠色線條代表 String 物件的內容指向。 紅色線條代表地址指向。
例程1與例程2的分析
如上圖所示。首先說一下 jdk6中的情況,在 jdk6中上述的所有列印都是 false 的,因為 jdk6中的常數池是放在 Perm 區中的,Perm區和正常的 JAVA Heap 區域是完全分開的。上面說過如果是使用引號宣告的字串都是會直接在字串常數池中生成,而 new出來的 String 物件是放在 JAVA Heap 區域。所以拿一個 JAVA Heap 區域的物件地址和字串常數池的物件地址進行比較肯定是不相同的,即使呼叫String.intern方法也是沒有任何關係的。
在 Jdk6 以及以前的版本中,字串的常數池是放在堆的Perm區的,Perm區是一個類靜態的區域,主要儲存一些載入類的資訊,常數池,方法片段等內容,預設大小隻有4m,一旦常數池中大量使用 intern 會產生java.lang.OutOfMemoryError:PermGen space錯誤的。在 jdk7 的版本中,字串常數池已經從Perm區移到正常的Java Heap區域了。為什麼要移動,Perm 區域太小是一個主要原因,當然據訊息稱jdk8已經直接取消了Perm區域,而新建立了一個元區域。應該是jdk開發者認為Perm區域已經不適合現在 JAVA的發展了。字串常數池移動到JAVA Heap區域,現在解釋為什麼會有上述的列印結果。
例程1的分析
1.String s1 = new String("1");
分析:這行程式碼生成了2個物件(常數池中的“1” 和 JavaHeap 中的字串物件)。s.intern(); 這一句是 s1 物件去常數池中尋找後發現 “1” 已經在常數池裡了。
此時s1指向的是Java Heap中的字串物件。
2.String s2 = "1";
分析:這行程式碼生成一個 s2的參照指向常數池中的“1”物件。 結果就是 s1 和 s2 的參照地址不同。
3.String s3 = new String("1") + new String("1");
分析:這行程式碼生成了2個物件(字串常數池中的“1” 和 Java Heap中的 s3 參照指向的物件“11”(中間還有2個匿名的new String("1")我們不討論它)。
此時s3 是Java Heap中的字串物件的參照,物件內容是”11″,此時常數池中是沒有 “11”物件的。
4.s3.intern();
分析:這行程式碼將 s3中的"11"字串放入String 常數池中,因為此時常數池中不存在"11"字串,因此常規做法是跟 jdk6 圖中表示的那樣,在常數池中生成一個"11"的物件,關鍵點是 jdk7 中常數池不在Perm區域,而是在堆中了。常數池中不需再儲存一份物件了,可以直接儲存堆中的參照。這份參照指向s3參照的物件。 也就是說參照地址是相同的。
此時,s3是Java Heap中的字串物件的參照,物件內容是”11″,此時常數池中是有 “11”物件,它儲存的就是s3參照地址。
5.String s4 = "11";
這行程式碼”11″是顯式宣告的,因此會直接去常數池中建立,建立時發現已經有這個物件了。
此時:s4 == 常數池的“11”物件參照 == s3參照物件的參照
例程2的分析
String s1 = new String("1");
s1.intern();
String s2 = "1";
分析:s1.intern();,這一句往後放也不會有什麼影響了,因為物件池中在執行第一句程式碼String s = new String("1");的時候已經生成“1”物件了。下邊的s2宣告都是直接從常數池中取地址參照的。 s1 和 s2 的參照地址是不會相等的。
String s3 = new String("1") + new String("1");
分析:這行程式碼生成了2個物件(字串常數池中的“1” 和 Java Heap中的 s3 參照指向的物件“11”(中間還有2個匿名的new String("1")我們不討論它)。
此時s3 是Java Heap中的字串物件的參照,物件內容是”11″,此時常數池中是沒有 “11”物件的。
String s4 = "11";
分析:宣告 s4 的時候常數池中是不存在“11”物件的,執行完後,s4是常數池裡“11“物件的參照。
s3.intern();
分析:此時常數池中“11”物件已經存在了,不會有任何操作,s3仍然是堆中String物件的參照。因此 s3 != s4
package org.example.a; import java.util.Random; public class Demo { static final int MAX = 1000 * 10000; static final String[] arr = new String[MAX]; public static void main(String argv[]) { Integer[] DB_DATA = new Integer[10]; Random random = new Random(10 * 10000); for(int i = 0; i < DB_DATA.length; i++){ DB_DATA[i] = random.nextInt(); } long t = System.currentTimeMillis(); for(int i = 0; i < MAX; i++){ //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])); arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern(); } System.out.println((System.currentTimeMillis() -t) + "ms"); System.gc(); } }
上述程式碼是一個演示程式碼,其中有兩條語句不一樣,一條是使用 intern,一條是未使用 intern。
執行的引數是:-Xmx2g -Xms2g -Xmn1500M
不用intern
2160ms
使用intern
826ms
通過上述結果,我們發現不使用 intern 的程式碼生成了1000w 個字串,佔用了大約640m 空間。 使用了 intern 的程式碼生成了1345個字串,佔用總空間 133k 左右。其實通過觀察程式中只是用到了10個字串,所以準確計算後應該是正好相差100w 倍。雖然例子有些極端,但確實能準確反應出 intern 使用後產生的巨大空間節省。
細心的同學會發現使用了 intern 方法後時間上有了一些增長。這是因為程式中每次都是用了 new String 後, 然後又進行 intern 操作的耗時時間,這一點如果在記憶體空間充足的情況下確實是無法避免的,但我們平時使用時,記憶體空間肯定不是無限大的,不使用 intern佔用空間導致 jvm 垃圾回收的時間是要遠遠大於這點時間的。 畢竟這裡使用了1000w次intern 才多出來1秒鐘多的時間。
到此這篇關於詳解Java String中intern方法的原理與使用的文章就介紹到這了,更多相關Java String intern方法內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45