堆(Heap)
一個JVM執行個體隻存在一個堆記憶體,堆記憶體的大小是可以調節的。
類加載器讀取了類檔案後,需要把類、方法、常變量放到堆記憶體中,儲存所有引用類型的真是資訊,以友善器執行,堆記憶體分為3部分:
- Yong Generation Space 新生區: Young/New
- Tenure Generation Space 養老區:Old/Tenure
- Permanent Space 永久區:Perm
Java7之前:
Java8:
JDK1.8之後為metaspace替代永久代:
新生區是類的誕生、成長、消亡的區域,一個類在這裡産生,應用,最後被垃圾回收器收集,結束生命。新生區又分為兩部分:伊甸區(Eden Space)和幸存者區(Survivor space),所有的類都是再伊甸區被new出來的。
幸存區又兩個:0區(Survivoa 0 Space)和1區(Survivor 1 space)。當伊甸的空間用完時,程式又需要建立對象,JVM的垃圾回收器将對伊甸區進行垃圾回收(Minor GC),将伊甸區中的不再被其他對象所引用的對象進行銷毀。然後将伊甸區中的剩餘對象移動到幸存0區。若幸存0區也滿了,再對該區進行垃圾回收,然後移動到1區。如果1區也滿了,再移動到養老區。
若養老區也滿了,那麼這個時候将産生Major GC(Full GC),進行養老區的記憶體清理。若養老區執行了FUll GC之後發現依然無法進行對象的儲存,就會産生OOM異常『OutOfMemoryError』。
如果出現java.lang.OutOfMemoryError:Java heap space異常,說明Java虛拟機的堆記憶體不夠,原因有兩點:
- Java虛拟機的堆記憶體設定不夠,可以通過參數-Xms、-Xmx來調整。
- 代碼中建立了大量大對象,并且長時間不能被垃圾收集器收集(存在被引用)。
Java堆從GC的角度還可以細分為:新生代(Eden區、From Survivor區和To Survivor區) 和老年代。
MinorGC的過程(複制-》清空-》互換):
-
eden、SurvivorFrom複制到SurvivorTo,年齡+1
首先,當Eden區滿的時候會觸發第一次GC,把還活着的對象拷貝到SurvivorFrom區,當Eden區再次出發GC的時候會掃描Eden區和From區域,對這兩個區域進行垃圾回收,經過這次回收後還存貨的對象,則直接複制到To區域(如果有對象年齡已經達到了老年的标準,則複制到老年代),同時把這些對象的年齡+1.
-
清空eden、SurvivorFrom:
然後,清空Eden和SurivorFrom中的對象,也即複制之後有交換,誰空誰是to。
-
SurvivorTo和SurvivorFrom互換:
随後,SurvivorTo和SurvivorFrom互換,原SurvivorTo成為下一次GC時的SurvivorFrom區。部分對象會在From和To區域中複制來複制區,如此交換15次(由JVM參數MaxTenuringThreshold決定,這個參數預設為15),最終如果還是存活,就存入到老年代。
HotSpot記憶體管理
分代管理:
因為:不同對象的生命周期不同,98%的對象是臨時對象。
實際而言,方法區(Method Area)和堆一樣,是各個線程共享的記憶體區域,它用于存儲虛拟機加載的:類資訊+普通常量+靜态常量+編譯器編譯後的代碼等等,雖然JVM規範将方法區描述為堆的一個邏輯部分,但它卻還有一個别名叫Non-Heap(非堆),目的就是要和堆分開。
對于HotSpot虛拟機,很多開發者習慣将方法區稱之為『永久代』(Parmanent Gen),但嚴格本質上說兩者不同,或者說使用永久代來實作方法區而已,永久代是方法區(相當于是一個接口interface)的一個實作,jdk1.7的版本中,已經将原本放在永久代的字元串常量池移走。
永久區(java7之前):
永久存儲區是一個常駐記憶體區域,用于存放JDK自身所攜帶的Class,Interface的中繼資料,也就是說它存儲的是運作環境必須的類資訊,被裝載進此區的資料是不會被垃圾回收器回收掉的,關閉了JVM才會釋放此區所占用的記憶體。