Davids原理探究:JDK8將永久代(PermGen)替換爲元空間(MetaSpace)的原因

JDK8將永久代(PermGen)替換爲元空間(MetaSpace)的原因

關注可以查看更多粉絲專享blog~

什麼是方法區

方法區(Method Area)和Java堆一樣,是各個線程共享的內存區域,它用於存儲已經被虛擬機加載的類型信息、常量、靜態變量、即時編譯器編譯後的代碼緩存等數據。雖然《Java虛擬機規範》中把方法區描述爲堆的一個邏輯部分,但是它卻有着一個別名叫做“非堆”(Non-Heap),目的是與Java堆區分開。

永久代產生的原因

說到方法區不得不提一下“永久代”這個概念,尤其是在JDK8之前,許多Java程序員都習慣在HotSpot虛擬機上開發、部署程序,很多人都更願意把方法區稱呼爲“永久代”(Permanent Generation),或將二者混爲一談。本質二者並不是等價的,因爲僅僅是當時的HotSpot虛擬機設計團隊選擇把收集器的分代設計擴展至方法區,或者說使用永久代來實現方法區(J9和JRockit虛擬機是沒有永久代這個概念的,永久代相當於HotSpot針對於《Java虛擬機規範》中方法區的一種實現方式,如何實現方法區屬於虛擬機實現細節,不受《Java虛擬機規範》管束,並不統一要求),這樣HotSpot的垃圾回收器就能夠像管理Java堆一樣管理這部分內存,省去專門爲方法區編寫內存管理代碼的工作。

將永久代替換爲源空間的原因

  1. Oracle收購了BEA獲得了JRockit的所有權後,準備把JRockit中的優秀功能,譬如Java Mission Control管理工具,移植到HotSpot虛擬機時,因爲兩者對方法區實現的差異而面臨諸多困難。考慮到HotSpot未來的發展,在JDK6的時候HotSpot開發團隊就有放棄永久代,逐步改爲本地內存(Native Memory)來實現方法區的計劃了。而到了JDK7,已經把原本放在永久代的字符串常量、靜態變量等移出;到了JDK8,終於完全廢棄了永久代的概念,改用與JRockit、J9一樣的本地內存中實現元空間(Metaspace)來代替,把JDK7中永久代剩餘的內容(主要是類型信息)全部移到元空間中。
  2. 永久代這種設計導致了Java應用更容易遇到內存溢出的問題(永久代有-XX:MaxPermSize的上限,即使不設置也有默認大小,而J9和JRockit只要沒有觸碰到進程可用上限,例如32位系統中的4GB限制,就不會出問題。)
  3. 有極少數方法(例如String::intern())會因爲永久代的原因而導致不同虛擬機下有不同的表現
  4. 《Java虛擬機規範》對方法區的約束非常寬鬆,除了和Java堆一樣不需要連續的內存和可以選擇固定大小或者可擴展外,甚至還可以選擇不實現垃圾回收。

相對而言,垃圾收集行爲在這個區域是比較少出現的,但並非數據進入了方法區就如永久代的名字一樣,“永久”存在了。這區域的內存回收目標主要是針對常量池和對類型的卸載,一般來說這個區域的回收效果比較難令人滿意,尤其是類型的卸載,條件相當苛刻,但是這部分區域的回收有時又確實是有必要的。以前Sun公司的Bug list中,曾出現過的若干個嚴重Bug就是由於低版本的HotSpot虛擬機對此區域未完全回收而導致內存泄漏。
類型卸載條件(滿足以下三個條件代表可以回收,但是不一定會回收):

  1. 該類所有的實例都已經被回收,也就是 Java 堆中不存在該類的任何實例。
  2. 加載該類的 ClassLoader 已經被回收。
  3. 該類對應的 java.lang.Class 對象沒有在任何地⽅被引⽤,⽆法在任何地⽅通過反射訪問該類
    的⽅法

根據《Java虛擬機規範》的規定,如果方法區無法滿足新的內存分配需求時,將拋出OutOfMemoryError異常。

參考文獻:
《深入理解Java虛擬機》(第三版)

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