JAVA面試官:請說什麼是字符串常量池?

Java Java虛擬機 虛擬機 設計 斯蒂芬朱cPlusPlus 2019-07-03

Java設計者為String提供了字符串常量池以提高其性能,那麼字符串常量池的具體原理是什麼,我們需要帶著以下三個問題,去理解字符串常量池:

  1. 字符串常量池的設計意圖是什麼?
  2. 字符串常量池在哪裡?
  3. 如何操作字符串常量池?

字符串常量池的設計意圖是什麼?

字符串的分配,和其他的對象分配一樣,耗費高昂的時間與空間代價。

JVM為了提高性能和減少內存開銷,在實例化字符串常量的時候進行了一些優化:

為了減少在JVM中創建的字符串的數量,字符串類維護了一個字符串池,每當代碼創建字符串常量時,JVM會首先檢查字符串常量池;

如果字符串已經存在池中,就返回池中的實例引用;

如果字符串不在池中,就會實例化一個字符串並放到池中。Java能夠進行這樣的優化是因為字符串是不可變的,可以不用擔心數據衝突進行共享;

實現的基礎:

因為字符串是不可變的,可以不用擔心數據衝突進行共享;

運行時實例創建的全局字符串常量池中有一個表,總是為池中每個唯一的字符串對象維護一個引用,這就意味著它們一直引用著字符串常量池中的對象,所以,在常量池中的這些字符串不會被垃圾收集器回收。

我們來看下面一段代碼,就是從字符串常量池中獲取相應的字符串:

JAVA面試官:請說什麼是字符串常量池?

字符串常量池在哪裡?

在分析字符串常量池的位置時,首先得了解JVM內存模型,JVM內存區域分為線程共享區線程獨佔區

線程共享區包括 [堆] 和 [方法區]

線程獨佔區包括 [Java虛擬機棧]、[本地方法棧] 和 [陳程序計數器]

JAVA面試官:請說什麼是字符串常量池?

方法區:

存放加載的類信息、常量、靜態變量,靜態代碼塊等信息;

類信息包括類的版本、字段、方法、接口等,方法區也被稱為永久代。

程序計數器:

是一塊比較小的內存區域,是唯一一個不會發生OutOfMemoryError的區域,可以這樣理解方法進棧後,每一行代碼都有一個標識,程序按著標識往下執行。

Java虛擬機棧:

每個方法執行,都會創建一個棧幀,方法調用進棧,方法結束出棧; 棧幀裡面存放著局部變量表,操作數棧,動態鏈接以及方法出口等;

局部變量表裡面存放著基本數據類型,引用類型等; 棧幀伴隨著方法的開始而開始,結束而結束;

局部變量表所需的內存空間在編譯期間就完成了分配,在運行期間是不會改變的;

棧很容易出現StackOverFlowError,棧內存溢出錯誤,常見於遞歸調用;

本地方法棧和Java虛擬機棧:

其實是差不多的,但是也是有區別的Java虛擬機棧為Java方法服務,本地方法棧為native方法服務

堆:

功能單一,就是存儲對象的實例,堆其實又分新生代和老年代;

新生代又分Eden、Survivor01和Survivor02三個區域,垃圾收集器主要管理的區域,Eden區回收效率很高。

並不是所有的對象實例都會分配到堆上去,Java虛擬機棧也會分配。堆很容易出現OutOfMemoryError錯誤,內存溢出

如何操作字符串常量池?

JVM實例化字符串常量池時

JAVA面試官:請說什麼是字符串常量池?

String.intern()判斷這個常量是否存在於常量池。

如果存在{

判斷存在內容是引用還是常量{

如果是引用{

返回引用地址指向堆空間對象

}

如果是常量{

直接返回常量池常量

}

}

}

如果不存在{

將當前對象引用複製到常量池,並且返回的是當前對象的引用

}

通過new操作符創建的字符串對象不指向字符串池中的任何對象,但是可以通過使用字符串的intern()方法來指向其中的某一個。java.lang.String.intern()返回一個保留池字符串,就是一個在全局字符串池中有了一個入口。如果以前沒有在全局字符串池中,那麼它就會被添加到裡面。

相關推薦

推薦中...