目录
引言
Java虚拟机的垃圾回收(GC)机制是Java生态系统的基石之一,它自动管理内存分配与回收,极大减轻了开发者的负担。
一、分代收集理论
主流JVM虚拟机垃圾收集器遵循的核心理论,包含以下原则:
弱分代:新生代 ,存放新创建的对象,绝大多数对象都是“朝生夕灭”(生命周期短)。
强分代:老年代存放长期存活的对象,经历垃圾收集次数越多的对象,越难回收(生命周期长)。
基于分代收集理论,收集器需将Java堆划分为不同区域,并根据对象的年龄分配到不同区域存储。
二、基础垃圾收集算法
标记-清除算法
算法步骤:
标记阶段:从GC Roots出发,标记所有可达对象
清除阶段:遍历堆内存,回收未被标记的对象
复制算法
算法原理:
将内存分为大小相等的两块,使用其中一块,垃圾回收时把存活对象复制到另一块,清空已使用块。
标记-整理算法
算法流程:
标记阶段同标记-清除,将所有存活对象向一端移动,清理边界外内存。
当前虚拟机的垃圾收集都基于分代收集思想,根据对象存活周期的不同,将内存分为几个不同的区域,在不同的区域使用不同的垃圾收集算法。
例如:Heap 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。 在新生代中,每次收集都会有大量垃圾对象被回收,所以可以选择“标记-复制”算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。 在老年代中,对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以选择“标记-清除”或“标记-整理”算法进行垃圾收集。
三、经典垃圾收集器
3.1Serial收集器
Serial收集器是最基本、历史最悠久的垃圾收集器,采用“标记-复制”算法负责新生代的垃圾收集。
它是 Hotspot 虚拟机运行在客户端模式下的默认新生代收集器。 它是一个单线程收集器。它会使用一条垃圾收集线程去完成垃圾收集工作,并且它在进行垃圾收集工作的时候,必须暂停其他所有的工作线程,直到收集结束。 这样的设计,带来的好处就是:简单高效。对于内存资源受限制的环境,它是所有收集器中额外内存消耗最小的收集器。
适合单核处理器或处理器核心数较少的环境,每次收集几十 MB 甚至一两百 MB 的新生代内存,垃圾收集的停顿时间完全可以控制在十几毫秒或几十毫秒,最多一百多毫秒。
3.2Serial Old 收集器
Serial Old 收集器同样是一个单线程收集器,采用“标记-整理”算法负责老年代的垃圾收集,主要用于客户端模式下的HotSpot虚拟机使用。
3.3ParNew回收器
ParNew,是Serial的多线程版本,与CMS配合工作的新生代回收器,默认线程数=CPU核心数。
// 伪代码
void parNewCollect() {
stopAllApplicationThreads();
List<Thread> gcThreads = createGcThreads();
gcThreads.parallelForEach(thread -> {
markLiveObjects();
});
copySurvivors();
resumeApplicationThreads();
}
3.4Parallel Scavenge 收集器(新生代)
Parallel Scavenge 收集器是一款新生代收集器,使用“标记-复制”算法实现的多线程收集器 Parallel Scavenge 收集器与其它收集器的目标不同,CMS 等其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间。但是 Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。
吞吐量 = 运行用户代码时间 /(用户代码时间 + 运行垃圾收集时间)
四、并发垃圾回收器深度解析
4.1 CMS回收器
CMS收集器是一种以获取最短回收停顿时间为目标的收集器,基于“标记-清除”算法实现,是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
工作流程 整个过程包括四个步骤:
1. 初始标记(CMS initial mark):标记一下GC Roots能直接关联到的对象,速度很快。
2. 并发标记(CMS concurrent mark):从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
3. 重新标记(CMS remark):重新标记阶段,是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间长,远远比并发标记阶段时间短。
4. 并发清除(CMS concurrent sweep):清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的.
4.2G1回收器
G1(Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器、大容量内存的机器。它不再严格按照分代思想进行垃圾回收。
G1 采用局部性收集的思想,对于堆空间的划分,采用 Region 为单位的内存划分方式: G1 垃圾回收器把堆划分成若干个大小相同的独立区域(按照 JVM 的实现,Region 的数量不会超过2048个):每个 Region 都会代表某一种角色,H 、S 、E 、O 。E 代表 Eden 区,S 代表 Survivor 区,H 代表的是 Humongous(G1 用来分配大对象的区域,对于 Humongous 也分配不下的超大对象,会分配在连续的 N 个 Humongous 中),剩余的深蓝色代表的是 Old 区,灰色的代表的是空闲的 region 。
这种思想上的转变和设计,使得 G1 可以面向堆内存任何部分来组成回收集来进行回收,衡量标准不再是它属于哪个分代,而是哪块内存存放的垃圾最多,回收收益最大,这就是 G1 的混合 GC 模式。