当前位置:网站首页>JVM原理之完整的一次GC流程

JVM原理之完整的一次GC流程

2022-06-23 05:21:00 DayDayUp丶

JVM 的 GC 是指垃圾回收,主要是對堆內存的回收。本文將介紹 JVM 中一次完整的 GC 流程是怎樣的,首先拋出第一個問題,什麼樣的對象會是 JVM 回收的目標?

一、可達性分析算法(GC Roots)

有一種引用計數法,可以用來判斷對象被引用的次數,如果引用次數為0,則代錶可以被回收。

這種實現方式比較簡單,但對於循環引用的情况束手無策,所以 Java 采用了可達性分析算法

即判斷某個對象是否與 GC Roots 的這類對象之間的路徑可達,若不可達,則有可能成為回收對象,被判定為不可達的對象要成為可回收對象必須至少經曆兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。

在 Java 中,可作為 GC Roots 的對象包括以下幾種:

  1. 虛擬機棧(本地變量錶)中引用的對象
  2. 方法區中類靜態屬性引用的對象
  3. 方法區中常量引用的對象
  4. 本地方法棧中引用的對象

二、JVM中的堆結構

JVM 中的堆可劃分為兩大部分,新生代和老年代,大小比例為1:2,如下:

其中,新生代分為 Eden 區和  Survivor 區, Survivor 幸存者區又分為大小相等的兩塊 from 和 to 區。這便是 JVM 中堆的結構和各部分默認的比例,當然這些比例都可通過對應 JVM 參數來調整。

2.1 為何新生代要分為三個區

這裏需要介紹新生代的垃圾回收算法——複制算法。該算法的核心是將可用內存按容量劃分為大小相等的兩塊,每次回收周期只用其中一塊,當這一塊的內存用完,就將還存活的對象複制到另一塊上面,然後把已使用過的內存空間清理掉。

  • 優點:不必考慮內存碎片問題;效率高。
  • 缺點:可用容量减少為原來的一半,比較浪費。

【最優設置】:根據權威數據分析,90%的對象都是朝生夕死的,所以采用10%的空間用作交換區,因為交換區必須要有等量的兩個,所以采用複制算法中新生代中三個區默認分配比例為8:1:1。

2.2 新生代對象的分配和回收

(1)基本上新的對象優先在 Eden 區分配;

(2)當 Eden 區沒有足够空間時,會發起一次 Minor GC

(3)Minor GC 回收新生代采用複制回收算法的改進版本,即

  • from 區和 to 區的兩個交換區,這兩個區只有一個區有數據
  • 采用8:1:1的默認分配比例(-XX:SurvivorRatio默認為8,代錶 Eden 區與 Survivor 區的大小比例)

2.3 老年代對象的分配和回收

(1)老年代的對象一般來自於新生代中的長期存活對象。這裏有一概念叫做年齡閾值,每個對象定義了年齡計數器,經過一次 Minor GC (在交換區)後年齡加1,對象年齡達到15次後將會晋昇到老年代,老年代空間不够時進行 Full GC。當然這個參數仍是可以通過 JVM 參數(-XX:MaxTenuringThreshold,默認15)來調整。

(2)大對象直接進入老年代。即超過 Eden 區空間,或超過一個參數值(-XX:PretenureSizeThreshold=30m,無默認值)。這樣做的目的是避免在Eden區及兩個Survivor區之間發生大量的內存複制。

(3)對象提前晋昇到老年代(組團)。動態年齡判定:如果在 Survivor 區中相同年齡所有對象大小總和大於 Survivor 區大小的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,而無須等到自己的晋昇年齡。

三、JVM完整的GC流程

對象的正常流程:Eden 區 -> Survivor 區 -> 老年代

新生代GC:Minor GC;老年代GC:Full GC,比 Minor GC 慢10倍。

【總結】:內存區域不够用了,就會引發GC,JVM 會“stop the world”,嚴重影響性能。Minor GC 避免不了,Full GC 盡量避免。

【處理方式】:保存堆棧快照日志、分析內存泄漏、調整內存設置控制垃圾回收頻率,選擇合適的垃圾回收器等。

原网站

版权声明
本文为[DayDayUp丶]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/06/202206230323328931.html