淺談JVM - 內存結構(一)- java7 到 java8 內存結構的變化

回顧java程序執行流程java

如上圖所示,首先Java源代碼文件(.java後綴)會被Java編譯器編譯爲字節碼文件(.class後綴),而後由JVM中的類加載器加載各個類的字節碼文件,加載完畢以後,交由JVM執行引擎執行。在整個程序執行過程當中,JVM會用一段空間來存儲程序執行期間須要用到的數據和相關信息,這段空間通常被稱做爲Runtime Data Area(運行時數據區),也就是咱們常說的JVM內存。所以,在Java中咱們經常說到的內存管理就是針對這段空間進行管理(如何分配和回收內存空間)。程序員

根據 JVM 規範,JVM 內存共分爲虛擬機棧、堆、方法區、程序計數器、本地方法棧五個部分。數組

名稱 特徵 做用 配置參數 異常
程序計數器 佔用內存小,線程私有,生命週期與線程相同 大體爲字節碼行號指示器
虛擬機棧 線程私有,生命週期與線程相同,使用連續的內存空間 Java 方法執行的內存模型,存儲局部變量表、操做棧、動態連接、方法出口等信息 -Xss StackOverflowError/OutOfMemoryError
線程共享,生命週期與虛擬機相同,能夠不使用連續的內存地址 保存對象實例,全部對象實例(包括數組)都要在堆上分配 -Xms -Xsx -Xmn OutOfMemoryError
方法區 線程共享,生命週期與虛擬機相同,能夠不使用連續的內存地址 存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據

-XX:PermSize:16M架構

-XX:MaxPermSize64M性能

/spa

-XX:MetaspaceSize=16M線程

- XX:MaxMetaspaceSize=64M代理

OutOfMemoryError
本地方法棧 線程私有 爲虛擬機使用到的 Native 方法服務 StackOverflowError/OutOfMemoryError

Hotspot Java7和Java8內存結構差別code

Java7的內存結構jwt

Java8的內存結構

Java7和Java8內存結構的不一樣主要體如今方法區的實現

方法區是java虛擬機規範中定義的一種概念上的區域,不一樣的廠商能夠對虛擬機進行不一樣的實現。

咱們一般使用的Java SE都是由Sun JDK和OpenJDK所提供,這也是應用最普遍的版本。而該版本使用的VM就是HotSpot VM。一般狀況下,咱們所講的java虛擬機指的就是HotSpot的版本。

PermGen(永久代)

  • Java7及之前版本的Hotspot中方法區位於永久代中。同時,永久代和堆是相互隔離的,但它們使用的物理內存是連續的,永久代的垃圾收集是和老年代捆綁在一塊兒的,所以不管誰滿了,都會觸發永久代和老年代的垃圾收集。

  • Java7中永久代中存儲的部分數據已經開始轉移到Java HeapNative Memory中了。好比,符號引用(Symbols)轉移到了Native Memory;字符串常量池(interned strings)轉移到了Java Heap;類的靜態變量(class statics)轉移到了Java Heap

  • 絕大部分Java程序員應該都見過java.lang.OutOfMemoryError: PremGen space異常。這裏的PermGen space其實指的就是方法區。不過方法區和PermGen space又有着本質的區別。前者是JVM的規範,然後者則是JVM規範的一種實現,而且只有HotSpot纔有PermGen space,而對於其餘類型的虛擬機,如JRockit(Oracle)J9(IBM)並無PermGen space。因爲方法區主要存儲類的相關信息,因此對於動態生成類的狀況比較容易出現永久代的內存溢出。而且JDK 1.8中永久代的參數PermSizeMaxPermSize已經失效。

Metaspace(元空間)

  • 對於Java8,HotSpot取消了永久代,那麼是否是就沒有方法區了呢?固然不是,方法區只是一個規範,只不過它的實現變了。

  • 在Java8中,元空間(Metaspace)登上舞臺,方法區存在於元空間(Metaspace)。同時,元空間再也不與堆連續,並且是存在於本地內存(Native memory)。

  • JDK1.7後對JVM架構進行了改造,將類元數據放到本地內存中,另外,將字符串常量池和靜態變量放到Java堆裏。HotSpot VM將會爲類的元數據明確分配和釋放本地內存。在這種架構下,類元信息就突破了原來 -XX:MaxPermSize的限制,如今可使用更多的本地內存。這樣就從必定程度上解決了原來在運行時生成大量類形成常常Full GC問題,如運行時使用反射、代理等。因此升級之後Java堆空間可能會增長。

  • 元空間的本質和永久代相似,都是對JVM規範中方法區的實現。不過元空間與永久代之間的最大區別在於:元空間並不在虛擬機中,而是使用本地內存。本地內存(Native memory),也稱爲C-Heap,是供JVM自身進程使用的。當Java Heap空間不足時會觸發GC,但Native memory空間不夠卻不會觸發GC。默認狀況下元空間是能夠無限使用本地內存的,但爲了避免讓它如此膨脹,JVM一樣提供了參數來限制它使用的使用。

    • -XX:MetaspaceSize,class metadata的初始空間配額,以bytes爲單位,達到該值就會觸發垃圾收集進行類型卸載,同時GC會對該值進行調整:若是釋放了大量的空間,就適當的下降該值;若是釋放了不多的空間,那麼在不超過MaxMetaspaceSize(若是設置了的話),適當的提升該值。

    • -XX:MaxMetaspaceSize,能夠爲class metadata分配的最大空間。默認是沒有限制的。

    • -XX:MinMetaspaceFreeRatio,在GC以後,最小的Metaspace剩餘空間容量的百分比,減小爲class metadata分配空間致使的垃圾收集。

    • -XX:MaxMetaspaceFreeRatio,在GC以後,最大的Metaspace剩餘空間容量的百分比,減小爲class metadata釋放空間致使的垃圾收集。

因此對於方法區,Java8以後的變化

  • 移除了永久代(PermGen),替換爲元空間(Metaspace)

  • 永久代中的class metadata(類元信息)轉移到了native memory(本地內存,而不是虛擬機)

  • 永久代中的interned Strings(字符串常量池) 和 class static variables(類靜態變量)轉移到了Java heap

  • 永久代參數(PermSize MaxPermSize)-> 元空間參數(MetaspaceSize MaxMetaspaceSize)

Java8爲何要將永久代替換成Metaspace?

  • 字符串存在永久代中,容易出現性能問題和內存溢出。

  • 類及方法的信息等比較難肯定其大小,所以對於永久代的大小指定比較困 難,過小容易出現永久代溢出,太大則容易致使老年代溢出。

  • 永久代會爲 GC 帶來沒必要要的複雜度,而且回收效率偏低。

  • Oracle 可能會將HotSpot 與 JRockit 合二爲一,JRockit沒有所謂的永久代。

歡迎關注公衆號,後續文章更新通知,一塊兒討論技術問題 。

公衆號二維碼

相關文章
相關標籤/搜索