1. 程式人生 > >後端---Java中的常量池(字串常量池、class常量池和執行時常量池)

後端---Java中的常量池(字串常量池、class常量池和執行時常量池)

在Java的記憶體分配中,總共3種常量池:

JDK1.7之前執行時常量池邏輯包含字串常量池存放在方法區, 此時hotspot虛擬機器對方法區的實現為永久代

JDK1.7 字串常量池被從方法區拿到了堆中, 這裡沒有提到執行時常量池,也就是說字串常量池被單獨拿到堆,執行時常量池剩下的東西還在方法區, 也就是hotspot中的永久代

JDK1.8 hotspot移除了永久代用元空間(Metaspace)取而代之, 這時候字串常量池還在堆, 執行時常量池還在方法區, 只不過方法區的實現從永久代變成了元空間(Metaspace) 
 

一.字串常量池(String Constant Pool):

字串常量池在Java記憶體區域的哪個位置?

在JDK6.0及之前版本,字串常量池是放在Perm Gen區(也就是方法區)中;
在JDK7.0版本,字串常量池被移到了堆中了。至於為什麼移到堆內,大概是由於方法區的記憶體空間太小了.

字串常量池是什麼?

  • 在HotSpot VM裡實現的string pool功能的是一個StringTable類,它是一個Hash表,預設值大小長度是1009;這個StringTable在每個HotSpot VM的例項只有一份,被所有的類共享。字串常量由一個一個字元組成,放在了StringTable上。
  • 在JDK6.0中,StringTable的長度是固定的,長度就是1009,因此如果放入String Pool中的String非常多,就會造成hash衝突,導致連結串列過長,當呼叫String#intern()時會需要到連結串列上一個一個找,從而導致效能大幅度下降;
  • 在JDK7.0中,StringTable的長度可以通過引數指定:
     

-XX:StringTableSize=66666 

1.3:字串常量池裡放的是什麼?

  • 在JDK6.0及之前版本中,String Pool裡放的都是字串常量;
  • 在JDK7.0中,由於String#intern()發生了改變,因此String Pool中也可以存放放於堆內的字串物件的引用。

二.class常量池(Class Constant Pool靜態常量池):

2.1:class常量池簡介:

我們寫的每一個Java類被編譯後,就會形成一份class檔案;class檔案中除了包含類的版本、欄位、方法、介面等描述資訊外,還有一項資訊就是常量池(constant pool table),用於存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References);
每個class檔案都有一個class常量池。

2.2:什麼是字面量和符號引用:

  • 字面量包括:1.文字字串 2.八種基本型別的值 3.被宣告為final的常量等;
  • 符號引用包括:1.類和方法的全限定名 2.欄位的名稱和描述符 3.方法的名稱和描述符。

三.執行時常量池(Runtime Constant Pool):

  • 執行時常量池存在於記憶體中,也就是class常量池被載入到記憶體之後的版本,不同之處是:它的字面量可以動態的新增(String#intern()),符號引用可以被解析為直接引用
  • JVM在執行某個類的時候,必須經過載入、連線、初始化,而連線又包括驗證、準備、解析三個階段。而當類載入到記憶體中後,jvm就會將class常量池中的內容存放到執行時常量池中,由此可知,執行時常量池也是每個類都有一個。在解析階段,會把符號引用替換為直接引用,解析的過程會去查詢字串常量池,也就是我們上面所說的StringTable,以保證執行時常量池所引用的字串與字串常量池中是一致的。
     

總結

 

  • 1.字串常量池在每個虛擬機器中只有一份,存放的是字串常量的引用值。(堆)
  • 2.class常量池是在編譯的時候每個class都有的,在編譯階段,存放的是常量的符號引用。(方法區)
  • 3.執行時常量池是在類載入完成之後,將每個class常量池中的符號引用值轉存到執行時常量池中,也就是說,每個class都有一個執行時常量池,類在解析之後,將符號引用替換成直接引用,與全域性常量池中的引用值保持一致。(方法區)
  • 方法區裡儲存著class檔案的資訊和執行時常量池,class檔案的資訊包括類資訊和class檔案常量池。

 

è¿éåå¾çæè¿°