JVM 如何 GC

JVM 如何 GC

管理對象

  • 引用技術算法
  • 給對象中添加一個引用計數器,每當有一個地方引用它時,計數器的值就加1
  • 當引用失效時,計數器的值就減1,任何時刻計數器為0的對象就是不可能在被使用了
  • 這種算法是很簡單的,而且早期很多面向對象語言中都採用這種方式,但是現在主流的Java虛擬機中並沒有採用這種方式來管理對象,其原因最主要的原因是它很難解決對象之間的相互循環引用。
  • 可達性分析算法
  • 通過一系列的稱謂“GC Roots"的對象作為起始點
  • 從這些節點開始向下搜索,搜索所有走過的路徑為引用鏈,當一個對象到GC Roots沒有任何引用鏈項鍊時,則證明此對象時不可用的!
JVM 如何 GC

  • 上面的這張圖,對象object5、object6、object7雖然互相沒有關聯,但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收的對象
  • 注意:Java語言中,可作為GC Roots的對象包括下面幾種:
  1. 虛擬機棧(棧幀中的本地變量表)中引用的對象
  2. 方法區中類靜態屬性引用的對象
  3. 方法區中常量引用的對象
  4. 本地方法棧中JNI(即一般說的Native方法)引用的對象

內存管理

  • 在程序運行過程當中,會創建大量的對象,這些對象,大部分是短週期的對象,小部分是長週期的對象
  • 對於短週期的對象,需要頻繁地進行垃圾回收以保證無用對象儘早被釋放掉
  • 對於長週期對象,則不需要頻繁垃圾回收以確保不進行無謂地垃圾掃描檢測。為解決這種矛盾,JVM的內存管理採用分代的策略:
  • 年輕代(Young Gen):
  • 存放新創建的對象
  • 內存大小相對會比較小,垃圾回收會比較頻繁
  • 年輕代分成1個Eden Space和2個Suvivor Space(命名為A和B)
  • 當對象在堆創建時,將進入年輕代的Eden Space。垃圾回收器進行垃圾回收時,掃描Eden Space和A Suvivor Space,如果對象仍然存活,則複製到B Suvivor Space,如果B Suvivor Space已經滿,則複製 Old Gen。掃描A Suvivor Space時,如果對象已經經過了幾次的掃描仍然存活,JVM認為其為一個Old對象,則將其移到Old Gen。掃描完畢後,JVM將Eden Space和A Suvivor Space清空,然後交換A和B的角色(即下次垃圾回收時會掃描Eden Space和BSuvivor Space。
  • Young Gen垃圾回收時,採用將存活對象複製到到空的Suvivor Space的方式來確保不存在內存碎片,採用空間換時間的方式來加速內存垃圾回收。
  • 年老代(Tenured Gen):
  • JVM認為比較old的對象(經過幾次的Young Gen的垃圾回收後仍然存在)
  • 內存大小相對會比較大,垃圾回收也相對沒有那麼頻繁(比如可能幾個小時一次)
  • 年老代主要採用壓縮的方式來避免內存碎片(將存活對象移動到內存片的一邊),當然,有些垃圾回收器(比如CMS垃圾回收器)出於效率的原因,可能會不進行壓縮。
  • 持久代(Perm Gen):
  • 存放類定義、字節碼和常量等很少會變更的信息

造成 full gc 的原因

  • new 了很多對象,沒有即時在主動釋放掉->Eden 內存不夠用->不斷把對象往 old 遷移->old 滿了->full gc​
JVM 如何 GC


分享到:


相關文章: