字符串內存內部

本文基于我對StackOverflow的回答 。 我正在嘗試解釋String類如何存儲文本,內部存儲和常量池如何工作。

這里要理解的要點是String Java對象與其內容– private value字段下的char[]之間的區別。 String基本上是char[]數組的包裝器,將其封裝并使其無法修改,因此String可以保持不變。 另外, String類還記住該數組的實際部分(請參閱下文)。 這一切都意味著您可以擁有兩個指向相同char[]不同String對象(相當輕量)。

我會告訴你一些例子,連同hashCode()的每個StringhashCode()內部的char[] value字段(我將其稱之為文本字符串從區分)。 最后,我將顯示javap -c -verbose輸出以及測試類的常量池。 請不要將類常量池與字符串文字池混淆。 它們并不完全相同。 另請參見了解常量池的javap輸出 。

先決條件

為了進行測試,我創建了一個實用程序方法來破壞String封裝:

private int showInternalCharArrayHashCode(String s) {final Field value = String.class.getDeclaredField("value");value.setAccessible(true);return value.get(s).hashCode();
}

它將打印char[] value hashCode() ,有效地幫助我們了解此特定String是否指向相同的char[]文本。

一個類中的兩個字符串文字

讓我們從最簡單的示例開始。

Java代碼

String one = "abc";
String two = "abc";

順便說一句,如果您只寫"ab" + "c" ,則Java編譯器將在編譯時執行串聯,并且生成的代碼將完全相同。 僅當在編譯時知道所有字符串時,此方法才有效。

類常量池
每個類都有自己的常量池 -常量值列表,如果它們在源代碼中多次出現,則可以重用。 它包括常見的字符串,數字,方法名稱等。 這是上面示例中常量池的內容:

const #2 = String   #38;    //  abc
//...
const #38 = Asciz   abc;

需要注意的重要事項是String常量對象( #2 )和字符串指向的Unicode編碼文本"abc"#38 )之間的區別。

字節碼
這是生成的字節碼。 請注意, one引用和two引用都分配有指向"abc"字符串的相同#2常量:

ldc #2; //String abc
astore_1    //one
ldc #2; //String abc
astore_2    //two

輸出量
對于每個示例,我將打印以下值:

System.out.println("one.value: " + showInternalCharArrayHashCode(one));
System.out.println("two.value: " + showInternalCharArrayHashCode(two));
System.out.println("one" + System.identityHashCode(one));
System.out.println("two" + System.identityHashCode(two));

這兩對相等并不奇怪:

one.value: 23583040
two.value: 23583040
one: 8918249
two: 8918249

這意味著不僅兩個對象都指向相同的char[] (下面的相同文本),所以equals()測試將通過。 但更重要的是, onetwo是完全相同的引用! 因此, one == two也是正確的。 顯然,如果onetwo指向同一個對象,則one.valuetwo.value必須相等。

文字和new String() ?

Java代碼
現在,我們都在等待該示例–一個字符串文字和一個使用相同文字的新String 。 這將如何運作?

String one = "abc";
String two = new String("abc");

在源代碼中兩次使用了"abc"常量這一事實應該給您一些提示……

類常量池與上面相同。

字節碼

ldc #2; //String abc
astore_1    //onenew #3; //class java/lang/String
dup
ldc #2; //String abc
invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
astore_2    //two

仔細地看! 第一個對象的創建方法與上面相同,不足為奇。 它只需要從常量池中常量引用已經創建的String#2 )。 但是,第二個對象是通過常規構造函數調用創建的。 但! 第一個String作為參數傳遞。 可以將其反編譯為:

String two = new String(one);

輸出量
輸出有點令人驚訝。 第二對表示對String對象的引用是可以理解的-我們創建了兩個String對象-一個在常量池中為我們創建,第二個是為two手動創建的。 但是,為什么第一對建議兩個String對象都指向同一個char[] value數組呢?

one.value: 41771
two.value: 41771
one: 8388097
two: 16585653

當您查看String(String)構造函數的工作原理時,這一點變得很清楚(此處已大大簡化):

public String(String original) {this.offset = original.offset;this.count = original.count;this.value = original.value;
}

看到? 在基于現有對象創建新的String對象時,它會重用 char[] valueString是不可變的,不需要復制已知永遠不會修改的數據結構。 而且,由于new String(someString)創建了現有字符串的精確副本,并且字符串是不可變的,因此顯然沒有理由同時存在兩者。
我認為這是一些誤解的線索:即使您有兩個String對象,它們仍可能指向相同的內容。 如您所見, String對象本身很小。

運行時修改和intern() ?

Java代碼
假設您最初使用了兩個不同的字符串,但是在進行一些修改之后,它們都是相同的:

String one = "abc";
String two = "?abc".substring(1);  //also two = "abc"

Java編譯器(至少是我的)不夠聰明,無法在編譯時執行此類操作,請看一下:

類常量池
突然我們以指向兩個不同常量文本的兩個常量字符串結尾:

const #2 = String   #44;    //  abc
const #3 = String   #45;    //  ?abc
const #44 = Asciz   abc;
const #45 = Asciz   ?abc;

字節碼

ldc #2; //String abc
astore_1    //oneldc #3; //String ?abc
iconst_1
invokevirtual   #4; //Method String.substring:(I)Ljava/lang/String;
astore_2    //two

拳頭弦照常構造。 通過首先加載常量"?abc"字符串,然后在其上調用substring(1)來創建第二個。

輸出量

這里不足為奇–我們有兩個不同的字符串,指向內存中兩個不同的char[]文本:

one.value: 27379847
two.value: 7615385
one: 8388097
two: 16585653

好吧,文本并沒有真正的不同equals()方法仍然會產生true 。 我們有兩個不必要的相同文本副本。
現在我們應該進行兩次練習。 首先,嘗試運行:

two = two.intern();

在打印哈希碼之前。 onetwo不僅指向同一文本,而且它們是相同的參考!

one.value: 11108810
two.value: 11108810
one: 15184449
two: 15184449

這意味著one.equals(two)one == two測試都將通過。 我們還節省了一些內存,因為"abc"文本在內存中僅出現一次(第二個副本將被垃圾回收)。
第二個練習略有不同,請查看以下內容:

String one = "abc";
String two = "abc".substring(1);

顯然onetwo是兩個不同的對象,指向兩個不同的文本。 但是輸出如何表明它們都指向同一個char[]數組?!

one.value: 11108810
two.value: 8918249
one: 23583040
two: 23583040

我將答案留給你。 它會教您substring()工作原理,這種方法的優點是什么以及何時會導致大麻煩 。

得到教訓

  • String對象本身相當便宜。 它指向的文本占用了大部分內存
  • String只是char[]的薄包裝,以保持不變性
  • new String("abc")作為內部文本表示被重用是不是真的那么貴。 但是還是要避免這樣的構造。
  • 從編譯時已知的常量值連接String時,連接由編譯器而不是由JVM完成
  • substring()有點棘手,但最重要的是,就使用的內存和運行時間而言,它都很便宜(在兩種情況下均保持不變)

參考:來自Java和社區博客的JCG合作伙伴 Tomasz Nurkiewicz的字符串內存內部結構 。


翻譯自: https://www.javacodegeeks.com/2012/07/string-memory-internals.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371708.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371708.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371708.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

關于inline-block 元素之間為何會產生間隔

關于inline-block 元素之間為何會產生間隔 現象&#xff1a; <body><input type"text"><input type"text"> </body> 在瀏覽器中的表現&#xff1a; 實時上不僅僅是 inline-block 會導致這種現象。 inline 也會導致。 那問題來了&a…

java 入參 是 枚舉_java 枚舉 參數傳遞

展開全部這樣做是不行的&#xff0c;原因是&#xff1a;Java中的對象實例化都是在堆中&#xff0c;如果是普通的類實例變量&#xff0c;比如在方法636f707962616964757a686964616f313333376166371中定義的普通類實例變量&#xff0c;傳到了方法2中&#xff0c;由于方法1和方法2…

loadView的使用總結

一、loadView 1. loadView什么時候被調用&#xff1f; 每次訪問UIViewController的view&#xff08;如 controller.view、self.view&#xff09;并且view為nil&#xff0c;loadView方法就會被調用 2. 有什么作用 loadView 方法是用來負責創建UIViewController的view 3. 默認實…

數據庫備份 java jar_Java實現數據庫備份并利用ant導入SQL腳本

?數據備份對于經常在運維部署方面的工作者來說&#xff0c;是一件相對簡單的事情&#xff0c;都可以通過某一個SQL工具進行備份&#xff0c;但是如果在項目運行當中&#xff0c;我們需要對數據進行實時&#xff0c;或者是每隔一星期&#xff0c;一個月&#xff0c;等等進行數據…

JSF簡單Ajax示例

今天&#xff0c;我們將看到一些使用JSF的Ajax簡單樣本。 如果要查看有關JSF / Web應用程序的其他文章&#xff0c;請單擊以下鏈接&#xff1a; 重定向后的JSF持久化對象和消息 &#xff0c; 使用JAAS和JSF進行用戶登錄驗證 &#xff0c; JSF&#xff1a;Converter and Bean Au…

常用的好用的window工具

1. FastStone Capture截圖錄屏軟件 百度軟件中心&#xff1a;http://rj.baidu.com/soft/detail/13504.html?ald 注冊企業版&#xff1a; 用戶名&#xff1a;c1ikm 注冊碼&#xff1a;AXMQX-RMMMJ-DBHHF-WIHTV 中文輸入亂碼解決方法&#xff1a; 2. Notepad文本編輯器&#xff…

表分區

http://www.cnblogs.com/leestar54/p/6225821.html轉載于:https://www.cnblogs.com/jouny/p/6262850.html

java飛鴿傳書_feige 飛鴿傳書源代碼java 實現不錯的聯系網絡編程的資料飛鴿傳書的GUI(java實現) - 下載 - 搜珍網...

我的飛鴿傳書/FileFilter.java我的飛鴿傳書/FileNameExtensionFilter.java我的飛鴿傳書/飛鴿傳書/classes/feige/About.class我的飛鴿傳書/飛鴿傳書/classes/feige/ConnectOthers$ReadMessageThread.class我的飛鴿傳書/飛鴿傳書/classes/feige/ConnectOthers.class我的飛鴿傳書…

JAXB和根元素

XmlRootElement是人們習慣于與JAXB&#xff08;JSR-222&#xff09;一起使用的注釋。 目的是將根元素與類唯一關聯。 由于JAXB類映射到復雜類型&#xff0c;因此一個類有可能對應于多個根元素。 在這種情況下&#xff0c;無法使用XmlRootElement &#xff0c;人們開始感到有些困…

python socket模塊實現udp通信_Python基于socket模塊實現UDP通信功能示例

Python基于socket模塊實現UDP通信功能示例本文實例講述了Python基于socket模塊實現UDP通信功能。分享給大家供大家參考&#xff0c;具體如下&#xff1a;一 代碼1、接收端import socket#使用IPV4協議&#xff0c;使用UDP協議傳輸數據ssocket.socket(socket.AF_INET, socket.SOC…

Hibernate緩存基礎知識

最近&#xff0c;我嘗試了休眠緩存。 在這篇文章中&#xff0c;我想分享我的經驗&#xff0c;并指出Hibernate Second Level Cache的一些細節。 在此過程中&#xff0c;我將指導您閱讀一些有助于實現緩存的文章。 讓我們從地面開始。 在休眠狀態下緩存 緩存功能旨在減少必要的…

TP3.2之WHERE組合條件處理

1、條件都是int類型&#xff1a; $User->where(type1 AND status1)->select(); 2、條件包含字符串類型&#xff1a; 使用3.1以上版本的話&#xff0c;使用字符串條件的時候&#xff0c;建議配合預處理機制&#xff0c;確保更加安全&#xff0c; $Model->where("i…

linux-ssh遠程后臺執行腳本-放置后臺執行問題(轉)

寫了一個監控負載的小腳本&#xff08;死循環&#xff0c;測試結束后再kill對應進程&#xff09;&#xff0c;因需要監控多臺服務器&#xff0c;所以在一臺服務器上使用ssh統一執行腳本遇到問題&#xff1a;使用ssh root172.16.146.20 /usr/local/luly/loadavg.sh 2 2 &執行…

python2.7輸入函數_Python2.7的用戶輸入函數有問題,無法讓這些輸入與程序一起工作...

我對python世界還是個新手&#xff0c;雖然我已經用php做了很多工作。。。這是我的案子。。。在我正在用python2.7為我的小程序編寫一些代碼。在在那個程序中&#xff0c;我需要2個用戶輸入&#xff0c;它們都是數字。在第一個數字不得大于11&#xff0c;也不得小于0。在第二個…

創建Java動態代理

Java動態代理機制提供了一種有趣的方式來創建代理實例。 不過&#xff0c;創建動態代理的步驟有些繁瑣&#xff0c;請考慮將代理用于審核服務實例的方法調用所花費的時間– public interface InventoryService {public Inventory create(Inventory inventory);public List<I…

html5有哪些新特性、移除了那些元素?如何處理HTML5新標簽的瀏覽器兼容問題?如何區分 HTML 和 HTML5?...

新特性&#xff1a; 1. 拖拽釋放(Drag and drop) API 2. 語義化更好的內容標簽&#xff08;header,nav,footer,aside,article,section&#xff09; 3. 音頻、視頻API(audio,video) 4. 畫布(Canvas) API 5. 地理(Geolocation) API 6. 本地離線存儲 localStorage 長期存儲數據&am…

Substring with Concatenation of All Words 題解

題意 You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters. For example, give…

java udp丟包_linux 系統 UDP 丟包問題分析思路

最近工作中遇到某個服務器應用程序 UDP 丟包&#xff0c;在排查過程中查閱了很多資料&#xff0c;總結出來這篇文章&#xff0c;供更多人參考。在開始之前&#xff0c;我們先用一張圖解釋 linux 系統接收網絡報文的過程。● 首先網絡報文通過物理網線發送到網卡● 網絡驅動程…

【SQL】分組數據,過濾分組-group by , having

學習筆記&#xff0c;原文來自http://blog.csdn.net/robinjwong/article/details/24845125 創建分組 - GROUP BY 分組是在SELECT語句的GROUP BY子句中建立的。它的作用是通過一定的規則將一個數據集劃分成若干個小的區域&#xff0c;然后針對若干個小區域進行數據處理。SELECT子…