Java 中的字符串常量池詳解。本站提示廣大學習愛好者:(Java 中的字符串常量池詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是Java 中的字符串常量池詳解正文
Java中的字符串常量池
Java中字符串對象創立有兩種情勢,一種為字面量情勢,如String str = "droid";,另外一種就是應用new這類尺度的結構對象的辦法,如String str = new String("droid");,這兩種方法我們在代碼編寫時都常常應用,特別是字面量的方法。但是這兩種完成其實存在著一些機能和內存占用的差異。這一切都是源於JVM為了削減字符串對象的反復創立,其保護了一個特別的內存,這段內存被成為字符串常量池或許字符串字面量池。
任務道理
現代碼中湧現字面量情勢創立字符串對象時,JVM起首會對這個字面量停止檢討,假如字符串常量池中存在雷同內容的字符串對象的援用,則將這個援用前往,不然新的字符串對象被創立,然後將這個援用放入字符串常量池,並前往該援用。
舉例解釋
字面量創立情勢
String str1 = "droid";
JVM檢測這個字面量,這裡我們以為沒有內容為droid的對象存在。JVM經由過程字符串常量池查找不到內容為droid的字符串對象存在,那末會創立這個字符串對象,然後將剛創立的對象的援用放入到字符串常量池中,而且將援用前往給變量str1。
假如接上去有如許一段代碼
String str2 = "droid";
異樣JVM照樣要檢測這個字面量,JVM經由過程查找字符串常量池,發明內容為”droid”字符串對象存在,因而將曾經存在的字符串對象的援用前往給變量str2。留意這裡不會從新創立新的字符串對象。
驗證能否為str1和str2能否指向統一對象,我們可以經由過程這段代碼
System.out.println(str1 == str2);
成果為true。
應用new創立
String str3 = new String("droid");
當我們應用了new來結構字符串對象的時刻,不論字符串常量池中有無雷同內容的對象的援用,新的字符串對象都邑創立。是以我們應用上面代碼測試一下,
String str3 = new String("droid");
System.out.println(str1 == str3);
成果如我們所想,為false,注解這兩個變量指向的為分歧的對象。
intern
關於下面應用new創立的字符串對象,假如想將這個對象的援用參加到字符串常量池,可使用intern辦法。
挪用intern後,起首檢討字符串常量池中能否有該對象的援用,假如存在,則將這個援用前往給變量,不然將援用參加並前往給變量。
String str4 = str3.intern();
System.out.println(str4 == str1);
輸入的成果為true。
疑問成績
條件前提?
字符串常量池完成的條件前提就是Java中String對象是弗成變的,如許可以平安包管多個變量同享統一個對象。假如Java中的String對象可變的話,一個援用操作轉變了對象的值,那末其他的變量也會遭到影響,明顯如許是不公道的。
援用 or 對象
字符串常量池中寄存的時援用照樣對象,這個成績是最多見的。字符串常量池寄存的是對象援用,不是對象。在Java中,對象都創立在堆內存中。
更新驗證,收到的許多評論也在評論辯論這個成績,我簡略的停止了驗證。 驗證情況
22:18:54-androidyue~/Videos$ cat /etc/os-release NAME=Fedora VERSION="17 (Beefy Miracle)" ID=fedora VERSION_ID=17 PRETTY_NAME="Fedora 17 (Beefy Miracle)" ANSI_COLOR="0;34" CPE_NAME="cpe:/o:fedoraproject:fedora:17" 22:19:04-androidyue~/Videos$ java -version java version "1.7.0_25" OpenJDK Runtime Environment (fedora-2.3.12.1.fc17-x86_64) OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)
驗證思緒:以下的Java法式讀取一個年夜小為82M的視頻文件,以字符串情勢停止intern操作。
22:01:17-androidyue~/Videos$ ll -lh | grep why_to_learn.mp4 -rw-rw-r--. 1 androidyue androidyue 82M Oct 20 2013 why_to_learn.mp4
驗證代碼
import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class TestMain { private static String fileContent; public static void main(String[] args) { fileContent = readFileToString(args[0]); if (null != fileContent) { fileContent = fileContent.intern(); System.out.println("Not Null"); } } private static String readFileToString(String file) { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(file)); StringBuffer buff = new StringBuffer(); String line; while ((line = reader.readLine()) != null) { buff.append(line); } return buff.toString(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != reader) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } }
因為字符串常量池存在於堆內存中的永遠代,實用於Java8之前。我們經由過程設置永遠代一個很小的值來停止驗證。假如字符串對象存在字符串常量池中,那末必定拋出java.lang.OutOfMemoryError permgen space毛病。
java -XX:PermSize=6m TestMain ~/Videos/why_to_learn.mp4
運轉證實法式沒有拋出OOM,其實這個不克不及很好的證實存儲的是對象照樣援用。
然則這個至多證實了字符串的現實內容對象char[]不寄存在字符串常量池中。既然如許的話,其實字符串常量池存儲字符串對象照樣字符串對象的援用反而不是那末主要。但小我照樣偏向於存儲的為援用。
優缺陷
字符串常量池的利益就是削減雷同內容字符串的創立,節儉內存空間。
假如硬要說弊病的話,就是就義了CPU盤算時光來換空間。CPU盤算時光重要用於在字符串常量池中查找能否有內容雷同對象的援用。不外其外部完成為HashTable,所以盤算本錢較低。
GC收受接管?
由於字符串常量池中持有了同享的字符串對象的援用,這就是說是否是會招致這些對象沒法收受接管?
起首成績中同享的對象普通情形下都比擬小。據我查證懂得,在晚期的版本中確切存在如許的成績,然則跟著弱援用的引入,今朝這個成績應當沒有了。
關於這個成績,可以詳細懂得這片文章interned Strings : Java Glossary
intern應用?
關於應用intern的條件就是你清晰本身確切須要應用。好比,我們這裡有一份上百萬的記載,個中記載的某個值屢次為美國加利福尼亞州,我們不想創立上百萬條如許的字符串對象,我們可使用intern只在內存中保存一份便可。關於intern更深刻的懂得請參考深刻解析String#intern。
總有破例?
你曉得上面的代碼,會創立幾個字符串對象,在字符串常量池中保留幾個援用麼?
String test = "a" + "b" + "c";
謎底是只創立了一個對象,在常量池中也只保留一個援用。我們應用javap反編譯看一下便可得知。
17:02 $ javap -c TestInternedPoolGC Compiled from "TestInternedPoolGC.java" public class TestInternedPoolGC extends java.lang.Object{ public TestInternedPoolGC(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]) throws java.lang.Exception; Code: 0: ldc #2; //String abc 2: astore_1 3: return
看到了麼,現實上在編譯時代,曾經將這三個字面量分解了一個。如許做現實上是一種優化,防止了創立過剩的字符串對象,也沒有產生字符串拼接成績。關於字符串拼接,可以檢查Java細節:字符串的拼接。
以上就是對Java 中字符串常量池的材料整頓,後續持續彌補相干材料,感謝年夜家對本站的支撐!