當我們實習一個字符串時,我們確保該字符串的所有用途都指向同一個實例。Interning字符串
我會假設底層字符串對象在堆中。
但是,存儲在存儲器中的引用變量在哪裏?
它是否具有與static
相同的行爲 - 其中引用存儲在permgen中,並且只有在類加載器(和應用程序)退出後才使字符串實例可用於gc?
當我們實習一個字符串時,我們確保該字符串的所有用途都指向同一個實例。Interning字符串
我會假設底層字符串對象在堆中。
但是,存儲在存儲器中的引用變量在哪裏?
它是否具有與static
相同的行爲 - 其中引用存儲在permgen中,並且只有在類加載器(和應用程序)退出後才使字符串實例可用於gc?
直到JDK 6,Intern'ed字符串被存儲在內存池中的一個稱爲Permanent Generation的地方,這是爲非用戶對象保留的JVM區域,如類,方法和其他內部JVM對象。這個區域的大小是有限的,並且通常比堆小得多。
從JDK 7開始,interned字符串不再分配在Java堆的永久生成中,而是分配在Java堆的主要部分(稱爲年輕人和老一代)中,以及其他對象由應用程序創建。此更改將導致更多數據駐留在主Java堆中,永久生成中的數據更少,因此可能需要調整堆大小。由於這種變化,大多數應用程序在堆使用中只會看到相對較小的差異,但是加載很多類或大量使用String.intern()方法的較大應用程序將會看到更顯着的差異。
對此的詳細解釋可以在此answer上找到。
當我們實習一個字符串時,我們確保該字符串的所有用法都指向同一個實例。
不完全是。當你這樣做:
String s2 = s1.intern();
你正在做的是確保s2
指String
在字符串池中。這不會影響s1
或任何其他String
引用或變量中的值。如果你想讓字符串的其他副本被實現,你需要明確地做到這一點......或者將實際的字符串引用分配給相應的變量。
我會假設底層字符串對象在堆中。
這是正確的。它可能位於「permgen」堆或常規堆中,具體取決於您使用的Java版本。但它總是「堆在一起」。
但是,存儲在存儲器中的引用變量在哪裏?
「引用變量」...即保存從調用intern()
...獲得的引用的引用變量與任何其他變量沒有區別。它可以是
jstring
變量或JNI代碼(類似於舉行的「別處」。)事實上,典型的JVM使用私有哈希表來保存到實習字符串的引用,它使用JVM的弱點如果沒有別的東西在使用它們,可以確保被攔截的字符串可以被垃圾回收。
它是否具有與靜態相同的行爲 - 其中引用存儲在permgen中,並且只有在類加載器(和應用程序)退出後才使得字符串實例可用於gc?
通常不......見上文。
在大多數Java平臺中,實際的字符串可以像其他字符串一樣被垃圾收集。如果被攔截的字符串存儲在「permgen」空間中,則可能需要較長的時間才能對該對象進行垃圾收集,因爲「permgen」不經常收集。但是,實字符串的生命週期並不與類加載器的生命週期等相關。
一個'static'字符串不一定是一個字符串文字。 OP在這一點上似乎也很困惑。 –
@TimBender - 正確...他很可能會。但是我沒有從他寫的內容中清楚地得到這種印象。 –
靜態引用不會存儲在perm gen中。 – SimonC
不確定你所說的「引用變量」是什麼意思。有一個interned字符串的目錄,但這是隱藏的,你不需要擔心它。任何需要使用interned字符串的引用都可以擁有您想要的任何存儲類。 –
@SimonC - 實際上,靜態引用可以在permgen中,因爲持有它們的靜態框架可以是一個permgen對象。 (對於靜態框架在permgen中它肯定是有意義的,因爲它的生命週期與加載類的類加載器相關聯!)它只是靜態變量引用的對象,通常不是。 –