Java方法區、永久代、元空間 究竟是什麼關係呢?

1.JVM內存模型簡介

  • ——堆是所有線程共享的,主要用來存儲對象。其中,堆可分爲:年輕代老年代兩塊區域。使用NewRatio參數來設定比例。對於年輕代,一個Eden區和兩個Suvivor區,使用參數SuvivorRatio來設定大小;
  • Java虛擬機棧/本地方法棧——線程私有的,主要存放局部變量表操作數棧動態鏈接方法出口等;
  • 程序計數器——同樣是線程私有的,記錄當前線程的行號指示器,爲線程的切換提供保障;
  • 方法區——線程共享的,主要存儲類信息常量池靜態變量JIT編譯後的代碼等數據。方法區理論上來說是堆的邏輯組成部分;
  • 運行時常量池——是方法區的一部分,用於存放編譯期生成的各種字面量和符號引用;

2.永久代和方法區的關係

涉及到內存模型時,往往會提到永久代,那麼它和方法區又是什麼關係呢?《Java虛擬機規範》只是規定了有方法區這麼個概念和它的作用,並沒有規定如何去實現它。那麼,在不同的 JVM 上方法區的實現肯定是不同的了。 同時大多數用的JVM都是Sun公司的HotSpot。在HotSpot上把GC分代收集擴展至方法區,或者說使用永久代來實現方法區。因此,我們得到了結論,永久代是HotSpot的概念,方法區是Java虛擬機規範中的定義,是一種規範而永久代是一種實現,一個是標準一個是實現。其他的虛擬機實現並沒有永久帶這一說法。在1.7之前在(JDK1.2 ~ JDK6)的實現中,HotSpot 使用永久代實現方法區,HotSpot 使用 GC分代來實現方法區內存回收,可以使用如下參數來調節方法區的大小:

-XX:PermSize
方法區初始大小
-XX:MaxPermSize
方法區最大大小
超過這個值將會拋出OutOfMemoryError異常:java.lang.OutOfMemoryError: PermGen

3.元空間

對於Java8, HotSpots取消了永久代,那麼是不是也就沒有方法區了呢?當然不是,方法區是一個規範,規範沒變,它就一直在。那麼取代永久代的就是元空間。它可永久代有什麼不同的?存儲位置不同,永久代物理是是堆的一部分,和新生代,老年代地址是連續的,而元空間屬於本地內存;存儲內容不同,元空間存儲類的元信息,靜態變量和常量池等併入堆中。相當於永久代的數據被分到了堆和元空間中。

4.Class文件常量池

Class 文件常量池指的是編譯生成的 class 字節碼文件,其結構中有一項是常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放。

  • 這裏的字面量是指字符串字面量和聲明爲 final 的(基本數據類型)常量值,這些字符串字面量除了類中所有雙引號括起來的字符串(包括方法體內的),還包括所有用到的類名、方法的名字和這些類與方法的字符串描述、字段(成員變量)的名稱和描述符;聲明爲final的常量值指的是成員變量,不包含本地變量,本地變量是屬於方法的。這些都在常量池的
    UTF-8 表中(邏輯上的劃分);
  • 符號引用,就是指指向 UTF-8 表中向這些字面量的引用,包括類和接口的全限定名(包括包路徑的完整名)、字段的名稱和描述符、方法的名稱和描述符。只不過是以一組符號來描述所引用的目標,和內存並無關,所以稱爲符號引用,直接指向內存中某一地址的引用稱爲直接引用;

5.運行時常量池

運行時常量池是方法區的一部分,是一塊內存區域。Class 文件常量池將在類加載後進入方法區的運行時常量池中存放。一個類加載到 JVM 中後對應一個運行時常量池,運行時常量池相對於 Class 文件常量池來說具備動態性,Class 文件常量只是一個靜態存儲結構,裏面的引用都是符號引用。而運行時常量池可以在運行期間將符號引用解析爲直接引用。可以說運行時常量池就是用來索引和查找字段和方法名稱和描述符的。給定任意一個方法或字段的索引,通過這個索引最終可得到該方法或字段所屬的類型信息和名稱及描述符信息,這涉及到方法的調用和字段獲取。

6.字符串常量池

字符串常量池是全局的,JVM 中獨此一份,因此也稱爲全局字符串常量池。運行時常量池中的字符串字面量若是成員的,則在類的加載初始化階段就使用到了字符串常量池;若是本地的,則在使用到的時候(執行此代碼時)纔會使用到字符串常量池。其實,“使用常量池”對應的字節碼是一個 ldc 指令,在給 String 類型的引用賦值的時候會先執行這個指令,看常量池中是否存在這個字符串對象的引用,若有就直接返回這個引用,若沒有,就在堆裏創建這個字符串對象並在字符串常量池中記錄下這個引用(jdk1.7)。String 類的 intern() 方法還可在運行期間把字符串放到字符串常量池中。JVM 中除了字符串常量池,8種基本數據類型中除了兩種浮點類型剩餘的6種基本數據類型的包裝類,都使用了緩衝池技術,但是 Byte、Short、Integer、Long、Character 這5種整型的包裝類也只是在對應值在 [-128,127] 時纔會使用緩衝池,超出此範圍仍然會去創建新的對象。其中:

  • 在 jdk1.6(含)之前也是方法區的一部分,並且其中存放的是字符串的實例;
  • 在 jdk1.7(含)之後是在堆內存之中,存儲的是字符串對象的引用,字符串實例是在堆中;
  • jdk1.8 已移除永久代,字符串常量池是在本地內存當中,存儲的也只是引用。

開心一刻
【沒聽清】
☞一對青年男女在公園約會時,女孩特別想放屁,她想了個辦法:
☞女:你聽過布穀鳥叫嗎?
☞男:沒聽過。
☞女:我給你學,布(放屁聲)-谷(口中發出的聲音)。
☞女:聽清了嗎?
☞男:放屁聲太大,沒聽清。

在這裏插入圖片描述

如果覺得不錯,幫忙點個贊,您的點贊將是我的動力!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章