1. 程式人生 > >面試官,Java8 JVM記憶體結構變了,永久代到元空間

面試官,Java8 JVM記憶體結構變了,永久代到元空間

在文章《JVM之記憶體結構詳解》中我們描述了Java7以前的JVM記憶體結構,但在Java8和以後版本中JVM的記憶體結構慢慢發生了變化。作為面試官如果你還不知道,那麼面試過程中是不是有些露怯?作為面試者,如果知曉這些變化,又將成為面試中的亮點。

如果在網路上搜索JVM記憶體結構,90%的可能會搜到Java7及以前的記憶體圖,本篇文章將會對JVM記憶體結構再次細化,深入理解Java8之後的內部變化。現在意識到關注公眾號“程式新視界”的好處了吧。在這裡可以不斷的重新整理你的知識和認知。

JVM記憶體結構的細化

再來看一下《JVM之記憶體結構詳解》中的記憶體結構圖。

為了更細化的講解,我們將該圖進行進一步的優化調整。針對java7及以前版本的細化。

看出變化了嗎?堆和方法區連在了一起,但這並不能說堆和方法區是一起的,它們在邏輯上依舊是分開的。但在物理上來說,它們又是連續的一塊記憶體。也就是說,方法區和前面講到的Eden和老年代是連續的。

在繼續進行下去之前,我們先來理解兩個概念:規範和實現。

規範和實現

針對Java虛擬機器的實現有專門的《Java虛擬機器規範》,在遵守規範的前提下,不同的廠商會對虛擬機器進行不同的實現。 就好比開發的過程中定義了介面,具體的介面實現大家可以根據不同的業務需求進行實現。

PS:大家都有必要了解一下《Java虛擬機器規範》,關注公眾號“程式新視界”,回覆“002”獲得Java SE 7的虛擬機器規範PDF版。

我們通常使用的Java SE都是由Sun JDK和OpenJDK所提供,這也是應用最廣泛的版本。而該版本使用的VM就是HotSpot VM。通常情況下,我們所講的java虛擬機器指的就是HotSpot的版本。

永久代(PermGen)

上面理解了規範和實現之後,來看認識一個概念“永久代(Permanet Generation,也稱PermGen)”。對於習慣了在HotSpot虛擬機器上開發、部署的程式設計師來說,很多都願意將方法區稱作永久代。

本質上來講兩者並不等價,僅因為Hotspot將GC分代擴充套件至方法區,或者說使用永久代來實現方法區。在其他虛擬機器上是沒有永久代的概念的。也就是說方法區是規範,永久代是Hotspot針對該規範進行的實現。

理解上面的概念之後,我們對Java7及以前版本的堆和方法區的構造再進行一下變動。

再重複一遍就是對Java7及以前版本的Hotspot中方法區位於永久代中。同時,永久代和堆是相互隔離的,但它們使用的實體記憶體是連續的。

永久代的垃圾收集是和老年代捆綁在一起的,因此無論誰滿了,都會觸發永久代和老年代的垃圾收集。

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

然後,在Java8中,時代變了,Hotspot取消了永久代。永久代真的成了永久的記憶。永久代的引數-XX:PermSize和-XX:MaxPermSize也隨之失效。

元空間(Metaspace)

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

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

本地記憶體(Native memory),也稱為C-Heap,是供JVM自身程序使用的。當Java Heap空間不足時會觸發GC,但Native memory空間不夠卻不會觸發GC。

針對Java8的調整,我們再次對記憶體結構圖進行調整。

元空間存在於本地記憶體,意味著只要本地記憶體足夠,它不會出現像永久代中“java.lang.OutOfMemoryError: PermGen space”這種錯誤。看上圖中的方法區,是不是“膨脹”了。

預設情況下元空間是可以無限使用本地記憶體的,但為了不讓它如此膨脹,JVM同樣提供了引數來限制它使用的使用。

  • -XX:MetaspaceSize,class metadata的初始空間配額,以bytes為單位,達到該值就會觸發垃圾收集進行型別解除安裝,同時GC會對該值進行調整:如果釋放了大量的空間,就適當的降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize(如果設定了的話),適當的提高該值。
  • -XX:MaxMetaspaceSize,可以為class metadata分配的最大空間。預設是沒有限制的。
  • -XX:MinMetaspaceFreeRatio,在GC之後,最小的Metaspace剩餘空間容量的百分比,減少為class metadata分配空間導致的垃圾收集。
  • -XX:MaxMetaspaceFreeRatio,在GC之後,最大的Metaspace剩餘空間容量的百分比,減少為class metadata釋放空間導致的垃圾收集。

永久代為什麼被替換了

思考一下,為什麼使用元空間替換永久代?

表面上看是為了避免OOM異常。因為通常使用PermSize和MaxPermSize設定永久代的大小就決定了永久代的上限,但是不是總能知道應該設定為多大合適, 如果使用預設值很容易遇到OOM錯誤。

當使用元空間時,可以載入多少類的元資料就不再由MaxPermSize控制, 而由系統的實際可用空間來控制。

更深層的原因還是要合併HotSpot和JRockit的程式碼,JRockit從來沒有所謂的永久代,也不需要開發運維人員設定永久代的大小,但是執行良好。同時也不用擔心執行效能問題了,在覆蓋到的測試中, 程式啟動和執行速度降低不超過1%,但是這點效能損失換來了更大的安全保障。

小結

經過上面的講解和演變,是不是對JVM的記憶體結構有了更深的理解了?可以和麵試官多聊一會兒了,畢竟面試官的時間也不多了。另外,走過路過不要錯過,該系列持續更新中。最後別忘了關注公眾號“程式新視界”獲得第一手資料。

原文連結:《面試官,Java8 JVM記憶體結構變了,永久代到元空間》

《面試官》系列文章:

  • 《JVM之記憶體結構詳解》
  • 《面試官,不要再問我“Java GC垃圾回收機制”了》
  • 《面試官,Java8 JVM記憶體結構變了,永久代到元空間》


程式新視界:精彩和成長都不容錯過