JDK8的JVM內存結構,元空間替代永久代成為方法區及常量池的變化


JVM的知識這里總結的很詳細:https://github.com/doocs/jvm/blob/master/README.md,因此在本博客也不會再對其中的東西重復總結了。

 

       現在很多文章關於JVM內存結構的說法模糊不清,這里記錄一下以前的一些比較模糊的JVM相關概念的重新認識。都是經過多處考證對比的。

MetaSpace代替Perm Gen

       即元空間代替了永久代,所以JVM關於永久代的參數也都作廢了,取而代之的是關於MetaSpace空間的參數。而且Mete Space是屬於直接內存。示意圖:

(圖片來源於網絡)

為什么要在直接內存里拿出來一塊內存作為元空間取代永久代呢?主要的說法有以下幾個:

(1)類及方法的信息等比較難確定其大小,因此對於永久代的大小指定比較困難,太小容易出現永久代溢出,太大則容易導致老年代溢出。

(2)永久代會為 GC 帶來不必要的復雜度,並且回收效率偏低。

即方便分配管理,因為直接內存空間比較充足;便於回收,因為永久代本來回收垃圾的事件發生概率很低,直接從JVM中拿出可以提高回收效率。

方法區與永久代的關系

        很多文章里喜歡把方法區等同與永久代,永久代既然沒了,方法區也就沒了。但我認為方法區只是一種邏輯上的概念,永久代指物理上的堆內存的一塊空間,這塊實際的空間完成了方法區存儲字節碼、靜態變量、常量的功能等等。既然如此,現在元空間也可以認為是新的方法區的實現了。

 

常量池隨永久代的變化

幾種常量池:

(1)靜態常量池:即*.class文件中的常量池,在Class文件結構中,最頭的4個字節存儲魔數,用於確定一個文件是否能被JVM接受,接着4個字節用於存儲版本號,前2個為次版本號,后2個主版本號,再接着是用於存放常量的常量池,由於常量的數量是不固定的,所以常量池的入口放置一個U2類型的數據(constant_pool_count)存儲常量池容量計數值。

這種常量池占用class文件絕大部分空間,主要用於存放兩大類常量:字面量和符號引用量,字面量相當於Java語言層面常量的概念,如文本字符串、基礎數據、聲明為final的常值等;符號引用則屬於編譯原理方面的概念,包括了如下三種類型的常量:類和接口的全限定名、字段名稱描述符、方法名稱描述符。類的加載過程中的鏈接部分的解析步驟就是把符號引用替換為直接引用,即把那些描述符(名字)替換為能直接定位到字段、方法的引用或句柄(地址)。

(2)運行時常量池:虛擬機會將各個class文件中的常量池載入到運行時常量池中,即編譯期間生成的字面量、符號引用,總之就是裝載class文件。為什么它叫運行時     常量池呢?因為這個常量池在運行時,里面的常量是可以增加的。如:“+”連接字符生成新字符后調用 intern()方法、生成基礎數據的包裝類型等等。

(3)字符串常量池 :字符串常量池可以理解為是分擔了部分運行時常量池的工作。加載時,對於class文件的靜態常量池,如果是字符串就會被裝到字符串常量池中。

(4)整型常量池:Integer,類似字符串常量池。管理-128--127的常量。類似的還有Character、Long等常量池(基本數據類型沒有哦,Double、Float也沒有常量池)

總結就是:

    class文件有常量池存放這個類的信息,占用了大多數空間。但是運行時所有加載進來的class文件的常量池的東西都要放到運行時常量池,這個運行時常量池還可以在運行時添加常量。字符串常量池、Integer等常量池則是分擔了運行時常量池的工作,

在永久代移除后,字符串常量池也不再放在永久代了,但是也沒有放到新的方法區---元空間里,而是留在了堆里(為了方便回收?)。運行時常量池當然是隨着搬家到了元空間里,畢竟它是裝類的重要等信息的,有它的地方才稱得上是方法區。

 


免責聲明!

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



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