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分代來實現方法區內存回收,可以使用如下參數來調節方法區的大小:

  1.  
    -XX:PermSize
  2.  
    方法區初始大小
  3.  
    -XX:MaxPermSize
  4.  
    方法區最大大小
  5.  
    超過這個值將會拋出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 已移除永久代,字符串常量池是在本地內存當中,存儲的也只是引用。

原文鏈接地址:https://blog.csdn.net/u011635492/article/details/81046174


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM