对象引用与垃圾回收算法
垃圾回收器概述
垃圾回收算法与垃圾回收器之间关系:
- 收集算法是内存回收的方法论
- 垃圾收集器就是内存回收的具体实现
- 根据具体应用场景选择适合自己的垃圾收集器
垃圾回收器
可以由不同的厂商、不同版本的 JVM 来实现。
垃圾回收器的分类
按垃圾回收线程数🐂
串行垃圾回收器:
- 串行回收指的是在同一时间段内只允许有一个 CPU 用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作结束
- 在诸如单 CPU 处理器或者较小的应用内存等硬件平台不是特别优越的场合,串行回收器的性能表现可以超过并行回收器和并发回收器
- 串行回收默认被应用在客户端的 Client 模式下的 JVM 中
并行垃圾回收器:
- 在并发能力比较强的 CPU 上,并行回收器产生的停顿时间要短于串行回收器
- 并行收集可以运用多个 CPU 同时执行垃圾回收,提升了应用的吞吐量
- 并行回收仍然与串行回收一样,采用独占式,使用了
“stop-the-world”
机制
按工作模式🐤
并发式垃圾回收器:
- 并发式垃圾回收器与应用程序现场交替工作
- 尽量减少应用程序的停顿时间
独占式垃圾回收器:
- 一旦运行就停止应用程序中所有的用户线程
- 直到垃圾回收过程完全结束
按碎片处理方式🐱👓
压缩式垃圾回收器:
- 压缩式垃圾回收器会在回收完成后对存活的对象进行压缩整理,清除回收后的碎片
- 分配对象时使用指针碰撞形式
非压缩式垃圾回收器:
- 非压缩式垃圾回收器不进行这部操作
- 分配对象时使用空闲列表形式
按工作的内存区分🐱🐉
- 年轻代垃圾回收器
- 老年代垃圾回收器
垃圾回收器性能指标
吞吐量
运行用户代码的时间占总运行时间的比例,比如:系统运行了100分钟,GC耗时1分钟,那么吞吐量就是 99/100 = 99%
,总运行时间:程序的运行时间 + 内存回收的时间。
垃圾收集开销
垃圾回收器负载,垃圾收集所用时间与总运行时间的比例。和吞吐量正好相反,上面的例子:1 / 100 = 1%
。
暂停时间
执行垃圾收集时,程序的工作线程被暂停的时间。因为垃圾回收导致应用程序暂停的时间。
收集频率
相对于应用程序的执行,收集操作发生的频率。内存占用:Java 堆区所占的内存大小。指垃圾回收器多长时间会运行一次。我们希望这个指标越低越好。
反应时间
指当一个对象成为垃圾后,多长时间内,它所占据的内存空间会被释放。
堆分配
不同的垃圾回收器对堆内存的分配方式可能不同的。一个良好的垃圾收集器应该有一个合理的堆内存区间划分。
主要关注
- 吞吐量
- 暂停时间
常见垃圾回收器
串行方式的 serial Gc
JDK1.3.1 一起来的是串行方式的 serial Gc,它是第一款 GC。Serial Serial Old。
ParNew 垃圾收集器
是 serial 收集器的多线程版本,并行垃圾回收器。
Parallel GC
JDK1.4.2 版本发布,Paralle l收集器其实就是 Serial 收集器的多线程版本,Parallel Scavenge Parallel Old,并行垃圾回收器,在 JDK1.6 时,成为默认的 GC。
CMS
JDM1.4 发布 CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,它是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。JDK14 中删除了 CMS,并发回收器。
G1
JDK1.7 开始引入,JDK9 中成为默认的垃圾回收器,替代 CMS,并发回收器。
Epsilon
JDK11 时引入,垃圾回收器控制内存分配,但是不执行任何垃圾回收工作。一旦 Java 的堆被耗尽,JVM 就直接关闭。设计的目的是提供一个完全消极的 GC 实现,分配有限的内存分配,最大限度降低消费内存占用量和内存吞吐时的延迟时间。
ZGC
JDK11 引入。
垃圾回收器组合关系
查看当前使用的垃圾回收器
-XX:+PrintCommandLineFlags
Serial / Serial Old 收集器
特点
serial (串行)垃圾收集器,是单线程收集器,这种垃圾收集器大家了解,现在已经不用串行的了。而且在限定单核 CPU 才可以用,对于交互较强的应用而言,这种垃圾收集器是不能接受的。一般在 JavaWeb 应用程序中是不会采用串行垃圾收集器的。
缺点
在执行垃圾收集时,会 STW(stop the world)
停止一切其他的线程,这里的单线程不仅仅是说明它只使用一条收集线程去完成垃圾收集工作,更强调的是它在进行垃圾收集时,必须暂停其他所有工作线程,直至收集结束,也即 “Stop The World”
。
优点
它也有着优于其他收集器的地方:简单而高效(与其他收集器的单线程相比)对于限定单个 CPU 的环境来说,Serial 收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得更高的单线程收集效率。
使用的垃圾回收算法
- serial 收集器是在新生代中使用,使用的是标记复制算法
- serial old 收集器在老年代使用,使用的是标记整理算法
可自行配置
年轻代
-XX:+UseSerialGC
老年代
-XX:+UseSerialOldGC
Serial 0ld
Serial 0ld 是运行在 Client 模式下默认的老年代的垃圾回收器,Serial 0ld 在 Server 模式下主要有两个用途。
- 与新生代的
Parallel Scavenge
配合使用 - 作为老年代
CMS
收集器的后备垃圾收集方案
ParNew 收集器
概述
Serial GC 是年轻代中的单线程垃圾收集器 ParNew 收集器是 Serial 收集器的多线程版本,Par 是 Parallel 的缩写 New 只能处理新生代,ParNew 收集器除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew 是很多 JVM 运行在 Server 模式下新生代的默认垃圾收集器。默认的收集线程数跟 CPU 核数相同,可以用参数 -XX:ParallelGCThreads
指定收集线程数,但是一般不推荐修改。
使用的垃圾回收算法:
ParNew 收集器在年轻代中同样也是采用复制算法、"Stop-the-World" 机制。老年代采用标记-整理算法。
参数配置
-XX:+UseParNewGC
Parallel Scavenge 收集器
概述
HotSpot 的年轻代中除了拥有 ParNew 收集器是基于并行回收的以外 Parallel Scavenge 收集器,同样也采用了复制算法、并行回收和 "Stop the World" 机制。 和 ParNew 收集器不同,Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量(Throughput) Parallel Scavenge 收集器关注点是吞吐量(高效率的利用CPU)CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值,它也被称为吞吐量优先的垃圾收集器。自适应调节策略也是 Parallel Scavenge 与 ParNew 一个重要区别。根据当前运行的情况,进行性能的监控,可以来调整内存的分配情况,可以达到一个最优的情况。
使用的垃圾回收算法
- 新生代采用复制算法
- 老年代采用标记-整理算法
Parallel Old
Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本。使用多线程和 “标记-整理” 算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器(JDK8 默认的新生代和老年代收集器)
参数配置
年轻代
-XX:+UseParallelGC
老年代
-XX:+UseParallelOldGC
CMS 收集器
概述
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,它是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。CMS 收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。停顿时间越短(低延迟)就越适合与用户交互的程序,良好的响应速度能提升用户体验。STW。
采用的算法
标记-清除
工作原理
初始标记
在这个阶段中,程序中所有的工作线程都将会因为 “Stop-the-World” 机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出 GC Roots 能直接关联到的对象。一旦标记完成之后就会恢复之前被暂停的所有应用线程。由于直接关联对象比较小,所以这里的速度非常快。
并发标记
从 GC Roots 的直接关联对象开始遍历整个对象的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
重新标记
由于在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。
并发清理
此阶段清理删除掉标记阶段判断的已经死亡的对象,释放内存空间。由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
并发重置
重置本次 GC 过程中的标记数据。
预备方案
非独占式
CMS 收集器采用的是并发回收(非独占式),但是在其初始化标记和再次标记这两个阶段中仍然需要执行 “Stop-the-World" 机制暂停程序中的工作线程,不过暂停时间并不会太长,因此可以说明目前所有的垃圾收集器都做不到完全不需要“Stop-the-World”,只是尽可能地缩短暂停时间。最耗费时间的并发标记与并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的。
预备方案
由于在垃圾收集阶段用户线程没有中断,所以在 CMS 回收过程中,还应该确保应用程序用户线程有足够的内存可用。CMS 收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,而是当堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在 CMS 工作过程中依然有足够的空间支持应用程序运行。要是 CMS 运行期间预留的内存无法满足程序需要,就会出现一次 “Concurrent Mode Failure”
失败,这时虚拟机将启动后备预案,临时启用 Serial 0ld 收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
CMS 优缺点
优点
- 并发收集
- 低延迟
缺点
- 会产生内存碎片,导致并发清除后,用户线程可用的空间不足。在无法分配大对象的情况下,不得不提前触发 Full GC
- CMS 收集器对 CPU 资源非常敏感。在并发阶段,它虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低
- CMS 收集器无法处理浮动垃圾
- 在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次 gc 再清理了
- 在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象
- CMS 将无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,从而只能在下一次执行 GC 时释放这些之前未被回收的内存空间
- 执行过程中的不确定性
- 一边回收,系统一边运行,也许没回收完就再次触发 full gc,也就是 "concurrent mode failure",此时会进入 stop the world,用 serial old 垃圾收集器来回收
参数配置
启用 cms
-XX:+UseConcMarkSweepGC
并发的 GC 线程数
-XX:ConcGCThreads
Full GC 之后做压缩整理(减少碎片)
-XX:+UseCMSCompactAtFullCollection
多少次 Full GC之 后压缩一次,默认是 0,代表每次 Full GC 后都会压缩一次
-XX:CMSFullGCsBeforeCompaction
当老年代使用达到该比例时会触发 Full GC(默认是 92,这是百分比)
-XX:CMSInitiatingOccupancyFraction
只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM 仅在第一次使用设定值,后续则会自动调整
-XX:+UseCMSInitiatingOccupancyOnly
在 CMS GC 前启动一次 minor gc,目的减少老年代对年轻代的引用,降低 CMS GC 的标记阶段时的开销,一般 CMS 的 GC 耗时 80% 都在标记阶段
-XX:+CMSScavengeBeforeRemark
表示在初始标记的时候多线程执行,缩短 STW
-XX:+CMSParallellnitialMarkEnabled
在重新标记的时候多线程执行,缩短 STW
-XX:+CMSParallelRemarkEnabled
三色标记
什么是三色标记
在并发标记的过程中,因为标记期间应用线程还在继续跑,对象间的引用可能发生变化,多标和漏标的情况就有可能发生,把 GC Roots 可达性分析遍历对象过程中遇到的对象, 按照 “是否访问过” 这个条件标记成以下三种颜色。
黑色
表示对象已经被垃圾收集器访问过, 且这个对象的所有引用都已经扫描过。黑色的对象代表已经扫描过,它是安全存活的,如果有其他对象引用指向了黑色对象,无须重新扫描一遍。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
灰色
表示对象已经被垃圾收集器访问过, 但这个对象上至少存在一个引用还没有被扫描过。
白色
表示对象尚未被垃圾收集器访问过。在可达性分析刚刚开始的阶段,所有的对象都是白色的,若在分析结束的阶段,仍然是白色的对象,即代表不可达。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具