前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JVM垃圾收集器全面剖析:算法、实现和优化

JVM垃圾收集器全面剖析:算法、实现和优化

原创
作者头像
用户5764929
发布于 2023-06-12 08:27:38
发布于 2023-06-12 08:27:38
2920
举报
文章被收录于专栏:代码修仙代码修仙

JVM的一个重要组件是垃圾收集器(GC,Garbage Collector)。垃圾收集器负责自动管理Java应用程序中的内存资源,以确保程序能够在充足的内存中运行

垃圾收集算法

垃圾收集算法主要用于判断对象是否还在使用,以及如何释放不再使用的对象所占用的内存。常见的垃圾收集算法包括

标记-清除算法(Mark-Sweep)

首先,将所有对象分为两类,一类是可达对象,指的是从根节点(例如局部变量、静态变量等)通过引用链可以访问到的对象;另一类是不可达对象,指的是无法通过根节点访问到的对象。标记-清除算法首先将所有可达对象进行标记,然后对标记的对象进行清除。

标记-清除算法分为两个阶段:标记阶段和清除阶段。

在标记阶段,垃圾收集器从根节点(如局部变量、静态变量等)出发,遍历所有可达对象(即从根节点通过引用链可以访问到的对象),并将这些对象进行标记。在实际实现中,标记可以通过在对象头中设置一个标志位来实现。

标记阶段完成后,进入清除阶段。清除阶段会遍历整个堆空间,释放未被标记的对象所占用的内存。这样,不再使用的对象所占用的内存就被回收了。

复制算法(Copying)

复制算法将内存分为两个相等的区域,同时只使用一个区域。当垃圾收集开始时,遍历所有可达对象,并将这些对象及其引用复制到另一个区域中。然后丢弃原区域的所有内容,将内存指针指向新的区域。这样,新区域中的内存就不再有碎片,可以提高内存分配效率。

复制算法主要解决了标记-清除算法中的内存碎片问题。复制算法将内存空间划分为两个相等的区域A和B(例如,半区),同时只使用一个区域(例如区域A)来存储对象。当垃圾收集开始时,垃圾收集器遍历所有可达对象,并将这些对象及其引用复制到另一个区域(例如区域B)中。接着,清空区域A的所有内容,将内存指针指向区域B。因此,活动对象被复制到新的区域后,内存空间变为连续的。

复制算法的主要问题是效率低下,因为需要将所有可达对象复制到新的内存区域,而且内存的一半空间始终是空闲的。

标记-整理算法(Mark-Compact)

标记-整理算法结合了标记-清除和复制算法的优点,主要用于解决老年代的垃圾收集问题。

首先,在标记阶段,垃圾收集器和标记-清除算法一样,从根节点开始遍历所有可达对象并进行标记。

接下来,在整理阶段,将所有被标记的对象向一端靠拢,并丢弃所有未标记的对象。这样一来,内存空间会变得连续,并消除了碎片问题。

标记-整理算法的主要优点是避免了复制算法中的空间浪费问题。但是,标记-整理算法在整理阶段需要移动对象,因此可能会导致一定的性能损耗。

垃圾收集器实现

Serial收集器

Serial收集器是一个单线程收集器,它在垃圾收集时,只使用一个线程去执行收集操作。在执行垃圾收集任务时,需要暂停其他所有的工作线程(称为Stop-The-World,简称STW),直到垃圾收集完成。

Serial收集器适用于对内存和CPU资源有限的场景,以及客户端应用程序。由于它是单线程的,因此Serial收集器在多核处理器环境下相对效率较低。

Serial收集器使用标记-复制算法进行新生代收集(Minor GC),使用标记-整理算法进行老年代收集(Major GC或Full GC)。

Parallel(Throughput)收集器

Parallel收集器(也称为吞吐量收集器)是一个并行收集器。与Serial收集器不同,Parallel收集器使用多个线程同时执行垃圾收集任务,这在多核处理器环境下可以高效利用CPU资源,从而提高垃圾收集的效率。

Parallel收集器主要目标是提高系统的吞吐量。它默认情况下会尽量利用可用的CPU核心来加速垃圾收集操作。类似于Serial收集器,Parallel收集器在垃圾收集期间也需要暂停其他所有工作线程。

Parallel收集器同样使用标记-复制算法进行新生代收集,使用标记-整理算法进行老年代收集。

CMS(Concurrent Mark Sweep)收集器

CMS收集器是一种以降低停顿时间为目标的收集器。与Serial和Parallel收集器不同,CMS收集器在执行垃圾收集任务时,并不需要暂停所有工作线程。CMS收集器通过并发标记-清除算法实现,其核心思想是将垃圾收集过程中的一部分工作与应用线程并发执行,从而减少单次垃圾收集引起的暂停时间。

CMS收集器的垃圾收集过程主要分为下面几个阶段:

  • 初始标记(Initial Mark,需要STW):标记与根节点直接关联的对象
  • 并发标记(Concurrent Mark):处理应用程序并发运行时标记可达对象
  • 重新标记(Remark,需要STW):修正并发标记阶段因应用程序运行引入的新引用关系
  • 并发清除(Concurrent Sweep):清除不可达对象

CMS收集器适用于对响应时间有较高要求的应用场景,比如Web服务器、缓存服务器等。

G1(Garbage-First)收集器

G1收集器是一种新型的收集器,旨在替代CMS收集器。G1收集器主要针对大堆内存的应用程序,它的主要目标是将程序的暂停时间控制在可预测的范围内,以便满足较严格的停顿时间要求。

G1收集器将整个Java堆划分为许多连续的内存区域(Region),这些区域大小相等且大小可配置。每个区域可以是Eden空间、Survivor空间或者Old空间的一部分。G1收集器通过对不同区域进行优先级排序,在垃圾收集时首先处理优先级较高的区域。

G1收集器的垃圾收集过程主要分为以下几个阶段:

  • 初始标记(Initial Mark,需要STW):标记与根节点直接关联的对象
  • 并发标记(Concurrent Mark):处理应用程序并发运行时标记可达对象
  • 最终标记(Final Mark,需要STW):修正并发标记阶段因应用程序运行引入的新引用关系
  • 筛选回收(Evacuation,部分需要STW):将选定的区域中的存活对象复制到其他空闲区域,释放回收区域的空间

G1收集器在实现上使用了标记-复制算法的变种,并引入了并发执行和增量回收的策略。这使得G1能够在保证较低的停顿时间的同时,也能实现较高的内存利用率。

垃圾收集优化策略

调整堆大小

合理地设置堆的初始大小(-Xms)和最大大小(-Xmx)可以避免频繁的垃圾收集。一般来说,初始大小设置较小可以降低应用启动所需的资源,而最大大小要根据应用程序的内存需求进行设置。堆大小不应该设置过大,以免造成内存浪费;也不应该设置过小,避免频繁触发垃圾收集导致性能下降。

调整新生代和老年代的比例

JVM堆内存分为新生代(包括Eden空间和Survivor空间)和老年代,两者的大小比例会影响垃圾收集的效率和频率。通过-Xmn参数设置新生代的大小,从而调整新生代和老年代的比例。

通常,新生代越大,Minor GC的时间间隔越长,但Full GC的时间也相对较长。因此,需要根据应用程序的对象生命周期特点来设置合适的新生代和老年代比例。

选择合适的垃圾收集器

根据应用程序的特点和需求,选择合适的垃圾收集器。例如,对于需要低延迟的应用程序,可以考虑使用CMS或G1收集器。对于需要高吞吐量的应用程序,可以选择Parallel收集器。对于单核处理器或内存资源有限的场景,可以使用Serial收集器。

监控和分析垃圾收集日志

监控并分析垃圾收集日志有助于发现性能瓶颈、内存泄漏或其他GC问题。通过查看日志,可以了解GC的频率、暂停时间、收集效果等指标,从而对垃圾收集进行优化。可以使用-Xloggc参数将GC日志输出到文件。

优化程序代码

减少不必要的对象创建和长生命周期对象。对于短生命周期的对象,可以尽量复用已有对象,减少创建新对象的开销,降低垃圾收集频率。例如,可以使用StringBuilder替代String进行字符串拼接操作。

对于长生命周期的对象,避免创建过多的大对象。大对象容易导致内存碎片问题,降低内存使用率。合理设计数据结构,避免不必要的引用关系,可减少长生命周期对象的数量。

使用JVM参数进行优化

-XX:SurvivorRatio 设置Eden空间和Survivor空间的比例。

-XX:MaxTenuringThreshold 控制对象在Survivor空间的晋升阈值,达到阈值后对象将进入老年代。

-XX:+DisableExplicitGC 禁止System.gc()显式调用,避免应用程序触发频繁的垃圾收集。

-XX:+UseStringDeduplication 开启Java 8中的字符串重复数据清理功能,减少相同字符串在内存中的存储。

这些策略需要根据应用程序的具体需求进行调整。垃圾收集优化是一个持续的过程,需要不断地进行调整和优化,才能使应用程序在性能和资源使用方面达到最佳效果。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
垃圾收集器ParNew-JVM(十)
上篇文章介绍了老年分担机制,在minorGC前会先判断一下老年代剩余空间是否大于需要移动的数据,如果大则直接fullGC,否则会看看是否配置了参数,没有则直接fullGC,有的话再算每次yangGC的平均值,再次比较老年代剩余空间,小于他的话则正常yangGC。
用户9919783
2023/09/05
1970
垃圾收集器ParNew-JVM(十)
JVM中常用的垃圾收集器和收集算法(超详解G1收集器)
主要为对象, 而提到对象, 我们需要知道对象什么时候被回收? 主要是引用失效的时候, 那什么时候引用失效, 下面就要讲讲对象的四种引用了
天下之猴
2024/09/20
5031
JVM中常用的垃圾收集器和收集算法(超详解G1收集器)
10. 系统分析垃圾收集器
垃圾收集常用的算法有三种。标记-清除算法,标记-复制算法,标记-整理算法。下面一个一个来看:
用户7798898
2021/10/25
5150
10. 系统分析垃圾收集器
【修正版】5张图带你彻底理解G1垃圾收集器
作为一款高效的垃圾收集器,G1在JDK7中加入JVM,在JDK9中取代CMS成为了默认的垃圾收集器。
jinjunzhu
2022/09/23
2.6K0
【修正版】5张图带你彻底理解G1垃圾收集器
JVM中各个垃圾收集器的使用场景
在以往的文章中(垃圾收集算法),我们讲述了JVM中垃圾收集算法,像标记-清除、标记-整理、复制、分代等算法,这些只是垃圾收集的方法论,今天要介绍的就是垃圾收集的具体实现---垃圾收集器。
用户1212940
2022/04/13
3770
JVM中各个垃圾收集器的使用场景
【JVM进阶之路】七:垃圾收集器盘点
在前面,我们已经了解了JVM的分代收集,知道JVM垃圾收集在新生代主要采用标记-复制算法,在老年代主要采用标记-清除和标记-整理算法。接下来,我们看一看JDK默认虚拟机HotSpot的一些垃圾收集器的实现。
三分恶
2021/04/01
6970
【JVM进阶之路】七:垃圾收集器盘点
JVM - 再聊GC垃圾收集算法及垃圾收集器
当前虚拟机的垃圾收集都采用分代收集算法 , 意思就是根据对象存活周期的不同将 java堆分为新生代和老年代,这样就可以根据各个年代的特点选择合适的垃圾收集算法。
小小工匠
2021/08/17
3650
图解常见 GC 算法和垃圾收集器
垃圾收集(Garbage Collection) 通常被称为"GC",它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。 jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的
CoderJed
2018/09/13
3.2K0
图解常见 GC 算法和垃圾收集器
JVM 系列(5) —— 垃圾收集器
这三个指标也被称为不可能的三角,即无法做到三者间的,随着硬件性能的提升,人们反而可以容忍内存占用的扩大,对延迟的容忍度反而降低。
求和小熊猫
2020/12/29
3260
面试官,不要再问我“Java 垃圾收集器”了
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
程序新视界
2019/10/28
5430
面试官,不要再问我“Java 垃圾收集器”了
面试官:你对JVM垃圾收集器了解吗?13连问你是否抗的住!
4、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
程序员追风
2020/05/15
2.7K0
面试官:你对JVM垃圾收集器了解吗?13连问你是否抗的住!
JVM垃圾收集—垃圾收集器及常见组合参数
串行收集器 Serial 和 Serial Old 只能有一个垃圾回收线程执行,用户线程暂停。(适用于内存较小的嵌入式设备)
向着百万年薪努力的小赵
2022/12/02
6720
JVM垃圾收集—垃圾收集器及常见组合参数
JVM活学活用——GC算法 垃圾收集器
概述 ----  垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。 jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。 对象存活判断 ----  判断对象是否存活一般有两种方式: 引用计数:每个对象有一个引用计数属
Janti
2018/04/10
6630
JVM活学活用——GC算法 垃圾收集器
JVM学习记录-垃圾收集器
先回顾一下上一篇介绍的JVM中常见几种垃圾收集算法: 标记-清除算法(Mark-Sweep)。 复制算法(Copying)。 标记整理算法(Mark-Compact)。 分代收集算法(Generati
纪莫
2018/07/06
3660
JVM之垃圾收集器
因为新生代和老年代采用回收算法的不同,垃圾收集器相应地也分为新生代收集器和老年代收集器。其中新生代收集器主要有Serial收集器、ParNew收集器和Parallel Scavenge收集器。老年代收集器主要有Serial Old收集器、Parallel Old收集器和CMS收集器。当然还包括了一款全新的、新生代老年代通用的G1收集器。各款收集器的搭配使用如下图所示,其中有连线的代表收集器可以搭配使用,没有连线的收集器表示不能搭配使用。
黑洞代码
2021/01/14
4670
JVM之垃圾收集器
GC算法和垃圾收集器
垃圾收集器在对堆回收之前,第一件事情就是要确定这些对象哪些还“存活”着,哪些对象已经“死去”(即不可能再被任何途径使用的对象)、
leobhao
2022/06/28
4020
GC算法和垃圾收集器
JVM之历代垃圾收集器详解
1.年轻代: Serial PartNew Parallel Scavenge
北洋
2021/12/28
3030
JVM之历代垃圾收集器详解
JVM 七种垃圾收集器
下图展示的是 7 种作用于不同分代的收集器,如果两种收集器之前有连接,表示它们可以配合使用。收集器所在的位置表示它是属于新生代收集器还是老年代收集器。
用户3596197
2019/03/01
7180
JVM 七种垃圾收集器
JVM:垃圾收集器
如果说收集算法是内存回收的方法论,那垃圾收集器就是内存回收的实践者。《Java虚拟机规范》中对垃圾收集器应该如何实现并没有做出任何规定,因此不同厂商、不同版本的虚拟机所包含的垃圾收集器都可能会有很大差别,不同的虚拟机一般也都会提供各种参数供用户根据自己的应用特点和要求组合出各个内存分代所使用的收集器。
HLee
2021/02/24
3610
JVM:垃圾收集器
【进阶之路】攻克JVM——JVM的垃圾收集器(三)
.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1{font-size:30px;margin-bottom:5px}.markdown-body h2{padding-bottom:12px;font-size:24px;border-bottom:1px solid #ececec}.markdown-body h3{font-size:18px;padding-bottom:0}.markdown-body h4{font-size:16px}.markdown-body h5{font-size:15px}.markdown-body h6{margin-top:5px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body img{max-width:100%}.markdown-body hr{border:none;border-top:1px solid #ddd;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;border-radius:2px;overflow-x:auto;background-color:#fff5f5;color:#ff502c;font-size:.87em;padding:.065em .4em}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{text-decoration:none;color:#0269c8;border-bottom:1px solid #d1e9ff}.markdown-body a:active,.markdown-body a:hover{color:#275b8c}.markdown-body table{display:inline-block!important;font-size:12px;width:auto;max-width:100%;overflow:auto;border:1px solid #f6f6f6}.markdown-body thead{background:#f6f6f6;color:#000;text-align:left}.markdown-body tr:nth-child(2n){background-color:#fcfcfc}.markdown-body td,.markdown-body th{padding:12px 7px;line-height:24px}.markdown-body td{min-width:120px}.markdown-body blockquote{color:#666;padding:1px 23px;margin:22px 0;border-left:4px solid #cbcbcb;background-color:#f8f8f8}.markdown-body blockquote:after{display:block;content:""}.markdown-body blockquote>p{margin:10px 0}.markdown-body ol,.markdown-body ul{padding-left:28px}.markdown-body ol li,.markdown-body
南橘
2021/04/02
3740
【进阶之路】攻克JVM——JVM的垃圾收集器(三)
相关推荐
垃圾收集器ParNew-JVM(十)
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档