文章目录
- [一、详解JVM中的分代回收(Generational Collection)](#一、详解JVM中的分代回收(Generational Collection))
-
- [1. 核心思想:分代假说](#1. 核心思想:分代假说)
- [2. 堆的区域划分](#2. 堆的区域划分)
- [3. 对象的分配与晋升路径(关键流程)](#3. 对象的分配与晋升路径(关键流程))
- [二、Minor GC、Mixed GC、Full GC的区别](#二、Minor GC、Mixed GC、Full GC的区别)
- 总结(面试向)
一、详解JVM中的分代回收(Generational Collection)
单纯使用任何一种基础垃圾回收算法(标记-清除、复制、标记-整理)都无法做到普适高效。现代虚拟机结合了多种算法,其核心思想就是"分代收集"
1. 核心思想:分代假说
分代收集建立在两个被实践广泛验证的假说之上:
- 弱分代假说:绝大多数对象都是"朝生夕死"的,生命周期极短
- 强分代假说:熬过越多次垃圾收集过程的对象,就越难以消亡
基于这两个假说,JVM的内存管理者得出一个推论:可以根据对象的存活周期将内存划分为不同的区域,并针对不同区域的特点,采用最适合的垃圾收集算法。 这就是分代收集的本质
2. 堆的区域划分
HotSpot虚拟机将Java堆(Heap)主要划分为两大部分:新生代(Young Generation)和老年代(Old Generation)
-
新生代(Young Generation):
- 特点:对象生命周期短,存活率低,GC频繁
- 内部划分 :为了配合复制算法 ,新生代内部又被划分为三个区域:
- 一块较大的 Eden区
- 两块较小的 Survivor区 ,通常被称为 From区(S0) 和 To区(S1)
- 默认比例 :
Eden : S0 : S1 = 8 : 1 : 1
。这个比例意味着新生代中,任何时刻总有90%的空间(Eden + 一个Survivor)是可用来分配对象的,只有10%的空间是"浪费"的
-
老年代(Old Generation):
- 特点 :存放生命周期长或者体积较大的对象,GC频率远低于新生代
- GC算法 :通常采用标记-清除 或标记-整理算法
-
整体比例:
- 默认情况下,新生代 : 老年代 ≈ 1 : 2
- 注意 :上述所有比例(
NewRatio
,SurvivorRatio
)都是可以通过JVM参数进行调整的,但在面试中,回答默认值即可
3. 对象的分配与晋升路径(关键流程)
一个普通Java对象从诞生到消亡,在分代堆中的完整路径如下:
-
诞生于Eden :绝大多数新创建的对象,首先会被分配在新生代的Eden区
-
首次GC(Minor GC) :当Eden区满时,会触发第一次垃圾收集,我们称之为Minor GC (或Young GC)
- 过程:JVM会扫描Eden区和当时作为From区的Survivor区(第一次GC时From区是空的),找出所有存活对象
- 复制 :将这些存活对象统一复制到To区
- 年龄增长:所有被复制的存活对象,其内部的年龄计数器会加1
- 清空:复制完成后,Eden区和From区被一次性清空。然后,From区和To区的角色互换(原来的To区变成下一次GC的From区)
-
在Survivor区之间流转:在此后的每一次Minor GC中,都会重复第2步的操作:将Eden区和当前From区中的存活对象,复制到To区,对象年龄加1,然后清空Eden和From
-
晋升到老年代:对象不会无限地在Survivor区流转。当满足以下任一条件时,对象就会从新生代"晋升"到老年代:
- 年龄阈值晋升 :当一个对象的年龄计数器达到一个阈值(默认为15 ,由
-XX:MaxTenuringThreshold
参数设定)时,它将在下一次Minor GC后被直接移入老年代 - 动态年龄判断:这是一个优化策略。如果在Survivor空间中,所有相同年龄的对象大小总和,大于Survivor空间的一半,那么年龄大于或等于该年龄的对象,就会被直接晋升到老年代,无需等待达到最大年龄阈值
- 大对象直接进入老年代 :如果一个对象所需内存非常大,超过了由
-XX:PretenureSizeThreshold
参数设定的值,它将不会在新生代分配,而是被直接分配到老年代。这是为了避免大对象在Eden区和两个Survivor区之间进行大量内存复制,降低GC效率 - 空间分配担保 :在发生Minor GC前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果大于,则Minor GC是安全的。如果小于,虚拟机会检查是否允许担保失败(HandlePromotionFailure),如果允许,则会继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试进行Minor GC,否则将改为进行一次Full GC
- 年龄阈值晋升 :当一个对象的年龄计数器达到一个阈值(默认为15 ,由
二、Minor GC、Mixed GC、Full GC的区别
这三种GC类型是根据其收集的内存范围来区分的
-
Minor GC / Young GC
- 定义 :指发生在新生代的垃圾收集动作
- 触发时机:通常是新生代的Eden区被占满时
- 特点 :
- 范围小:只清理新生代
- 频率高:因为对象"朝生夕死",新生代很快就会满
- 速度快:因为新生代存活对象少,复制算法效率高
- 会触发STW:虽然时间很短,但依然会短暂地暂停所有用户线程(Stop-The-World, STW)
-
Full GC / Major GC
- 定义 :指覆盖整个Java堆(新生代 + 老年代)以及方法区/元空间的垃圾收集
- 触发时机(应尽量避免) :
- 老年代空间不足
- 方法区/元空间(Metaspace)空间不足
- 显式调用
System.gc()
(不推荐,JVM可通过参数忽略) - Minor GC后,晋升到老年代的对象大小 > 老年代剩余空间(空间分配担保失败)
- 特点 :
- 范围大:清理整个堆内存
- 速度慢:需要处理的对象多,且老年代的回收算法(如标记-整理)本身就比较耗时
- STW时间长:暂停用户线程的时间是所有GC类型中最长的,可能会达到数百毫秒甚至数秒。这是线上应用需要极力避免和优化的重点
-
Mixed GC
- 定义 :指收集整个新生代 以及部分老年代区域的垃圾收集
- 出处 :这是G1(Garbage-First)及之后更先进的垃圾收集器(如ZGC、Shenandoah)特有的一种收集模式。它不是传统分代收集器(如Serial, Parallel, CMS)的概念
- 触发时机 :当整个堆的内存使用率达到某个阈值时(由
-XX:InitiatingHeapOccupancyPercent
参数控制,默认为45%),G1会启动并发标记周期,标记完成后,就开始进行多次Mixed GC - 特点 :
- 范围可控:G1将堆划分为多个大小相等的Region,Mixed GC会选择所有的新生代Region,以及一部分回收价值最高(即垃圾最多的)的老年代Region进行回收
- STW可预测 :G1的核心优势之一就是用户可以设定一个期望的STW停顿时间(通过
-XX:MaxGCPauseMillis
),G1会根据这个目标,在Mixed GC中有选择性地回收部分老年代Region,从而将停顿时间控制在目标范围内 - 目的:用多次、停顿时间可控的Mixed GC,逐步回收老年代的垃圾,从而避免或推迟代价高昂的Full GC的发生
总结(面试向)
- 说分代回收 :先讲分代假说 ,再讲区域划分 (新生代-Eden/S0/S1,老年代),最后详细描述一个对象的晋升全流程,并点出几种晋升的特殊情况(年龄、动态年龄、大对象)
- 说GC类型区别 :
- Minor GC:新生代,快,频繁,STW短
- Full GC:全堆+方法区,慢,STW长,是性能杀手,要避免
- Mixed GC :G1等收集器特有,全新生代+部分老年代,旨在用可控的STW回收老年代,避免Full GC