JVM GC(垃圾回收)机制

本文详细介绍了JVM的垃圾回收机制,包括垃圾的定义、GC算法(Mark-Sweep、Copying、Mark-Compact)和GC模型。重点讲解了分代模型,如Eden、Survivor和Tenured区的运作,并探讨了不同GC类型的实现,如Serial、Parallel Scavenge、CMS和G1,以及相关参数的设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JVM GC(垃圾回收)机制

一、垃圾?

本章概要:怎么确定一个对象是垃圾?

1.1 引用计数

实现:记录一个对象带的有引用,引用数为 0 的便是垃圾。

缺陷:无法解决循环引用的问题,如下图所示

1.2 Root Searching

❀ 哪些东西属于 Root?

  • JVM stack
  • static references in method area
  • runtime constant pool
  • native method stack
  • Clazz

即上图中黑线上面的四个部分

实现:从对象自身开始,向上搜索,不能到达 Root 的便是垃圾。

Java 使用该方案确定一个对象是否为垃圾

二、GC 的算法

本章概要

  • Mark-Sweep(标记清除)
  • Copying(拷贝)
  • Mark-Compact(标记压缩)

2.1 Mark-Sweep

实现:较为简单,把被标记为垃圾的内存回收即可

缺陷:容易形成内存碎片

2.2 Copying

实现:把内存分为两半,每次只用其中一半,回收之前,把有效的对象 copy 到另一侧,将刚才使用的一侧全部回收即可

缺陷:浪费空间

2.3 Mark-Compact

实现:在处理垃圾时,顺便对内存空间进行整理,将有效的对象往前堆放

缺陷:效率较低

三、GC 模型

本章概要:GC 算法在各种 GC 模型中的运用

GC 模型

  • 分代
  • 分区

3.1 堆内存逻辑分区

实现

  1. 新生对象进入 eden 区
  2. 垃圾回收时,有效对象进入其中一个 survivor 区,eden 区的内存空间全部被回收
  3. 整个 survivor 区采用 Copying 算法
  4. 每次垃圾回收时,survivor 区中仍没有被回收的对象计数器加一
  5. 当一个对象的计数的值到达一定次数时,该对象进入 tenured 区,即老年代
  6. tenured 区中,采用 Mark Compact 或 Mark Sweep 算法
3.1.1 对象的生存周期

3.1.2 解释:“栈?”

前置知识补充:首先,每个线程私有一个虚拟机栈(JVM stack)。线程中,每个方法在执行时,都会创建一个名为栈帧(stack frame)的数据结构,主要用于存放局部变量表、操作站、动态链接、方法出口等信息。方法的调用,则意味着栈帧在虚拟机栈中的压栈和弹栈过程。其中,弹栈意味着对其操作栈帧所属变量的清除。这时,某些对象的引用则会被断开,因而,这些对象将会被回收。除非,我们将某个对象的引用直接或间接地返回(传递)给其调用方等属于 Root 区的变量。

概念

  1. 逃逸:方法中创建的对象在有他人引用的情况下被回收
  2. 标量替换:可以被基础数据类型替换的对象(如:一个带有两个 int 类型变量的对象,可以被两个 int 类型的变量所替换)

应用:当某个对象在方法执行过程中准备创建时,会对其进行逃逸分析,若分析通过,则判断其是否可被进行标量替换,若可以,则对象直接生成在栈中。

优点:被创建的对象可随弹栈过程直接销毁,无需进入堆空间,减轻了垃圾回收的负担。

3.2 分代模型的具体实现

  • Serial/Serial Old
  • Parallel Scavenge/Parallel Old

3.2.1 Serial/Serial Old

Serial:工作于新生代,采用 Copying 算法

Serial Old:工作于老年代,采用 Mark-Compact 算法

实现:Stop the world(STW), then use a single thread to collect garbage.

缺陷:当内存空间变大,STW时间会变长,有较强的停顿感

3.2.2 Parallel Scavenge/Parallel Old(JDK 1.8 默认)

Parallel Scavenge:工作于新生代,采用 Copying 算法

Parallel Old:工作于老年代,采用 Mark-Compact 算法

实现:与 3.2.1 节相比,改为多个线程,垃圾回收时同样需要 STW

3.2.3 ParNew/CMS

ParNew:本质上与 Parallel Scavenge 一样,不过为了适应 CMS,对 Parallel Scavenge 做了一些增强。

CMS:全称为 Concurrent Mark Sweep,工作于老年代

从线程角度看:工作分为四个阶段:初始标记、并发标记、重新标记、并发清理

实现

  1. 初始标记:找到 Root 直接引用的对象
  2. 并发标记:边执行程序,边标记垃圾
  3. 重新标记:在并发标记中,会产生错标,因而利用重新标记来修正错标
  4. 并发清理:清理重新标记后确定为垃圾的对象

优点:大大降低了 STW 的耗时

缺陷:remark 阶段的三色机制,会导致漏标,因而不得不再次标记一遍。因此,存在导致严重的停顿隐患,所以没有任何一个版本的 JDK 默认使用该机制。

3.2.4 G1

自JDK 9 开始,G1 垃圾回收器作为了 JVM 的默认垃圾回收器。

详情可以参考 Oracle 的官方文档:Garbage-First Garbage Collector

3.3 概念及相关参数

-Xms:设置 JVM 初始分配的内存大小,默认是物理内存的 1/64

-Xmx:设置 JVM 最大分配的内存大小,默认是物理内存的 1/4

-Xmn:设置年轻代大小,例如 -Xmn2G 代表年轻代大小为 2G

3.4 GC 类型设置及相关参数

GC 类型设置

格式:-XX:+[参数]

参数描述
UseSerialGCSerial + Serial Old
UseParallelGCParallel Scavenge + Serial Old(PS Mark Sweep)
UseParallelOldGCParallel Scavenge + Parallel Old(JDK1.8 默认)
UseParNewGCParNew + Serial Old(在 JDK1.8 被废弃,JDK1.7 还可以使用)
UseConcMarkSweepGCParNew + CMS + Serial Old
UseG1GCG1

相关参数设置

格式:-XX:+[参数][=值]

参数描述
ParallelGCThreads指定ParNew收集器的线程数目。
MaxGCPauseMillisParallel Scavenge中最大垃圾收集停顿时间(毫秒数)
GCTimeRatioParallel Scavenge中设置吞吐量大小的参数。例如设置为99,允许的最大垃圾收集时间就是:1/(1+99)
UseAdaptiveSizePolicyParallel Scavenge中的参数,这是一个开关参数,会动态的调节新生代老年代中的参数,以提供合适的停顿时间或者最大的吞吐量。只需要设置基本的数据内存,如-Xms等
CMSInitiatingOccupancyFraction设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认值为 68%
UseCMSCompactAtFullCollectionCMS收集器默认开启,在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的。空间碎片问题没有了,但停顿时间不得不变长。设置CMS收集器在完成垃圾收集后再启动一次内存碎片整理
CMSFullGCsBeforeCompaction用于设置CMS收集器执行多少次不压缩的FullGC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都进行碎片整理)
SurvivorRatio新生代中 Eden 区与 Survivor 区域的比值
PretenureSizeThreshold大于这个参数的对象将直接进入老年代分配
MaxTenuringThreshold晋升到老年代的年龄
HandlePromotionFailure是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个 Eden 和 Survivor 的所有对象都存活的极端情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值