垃圾回收算法:

引用计数算法:记下引用数量,为0时回收。

实现简单,判定效率高,但难以解决对象之间相互循环引用问题。

可达性分析算法:通过GC Roots作为对象起点,向下搜索,经过的路径称为引用链,经过引用链可达到的对象称为可达,若对象不可达则称为不可用。

JVM采用的算法,采用一个OooMap数据结构实现。

JVM回收对象需要经过两次标记,在一次标记后进入F-Queue,后才会被回收,在这期间可能被救活(从新引用)。


 这里插一个Java引用的介绍。

强引用::普通的new 创造的对象,只要引用存在,就不会被回收。

软引用:通过SoftReference类创建,在内存不够时将其回收。

弱引用:在下次GC的时候回收。

虚引用:无法用于访问对象,只是在回收时有一个系统提示。


这里提一下,虽然方法区(永久代)的GC效果不显著,但是也是存在GC的,主要是针对废弃常量和无用的类,而针对频繁自定义classloader的场景虚拟机要具备类卸载功能、保证永久代不溢出。

分代收集方法

新生代:采用复制算法(内存区域分成两或多块,一块空闲,在GC过程中将留下的对象移动到空闲块,并紧密排布、另一块则清空,解决内存碎片问题),因为新生代的存活率很低,采用其他方法的移动耗损过高。

老年代:采用标记-清理或者标记-整理,因为老年代相对来说存活率比较高。


GC停顿

GC停顿是为了保证分析对象引用过程中,引用不发生变化。在生产环境中,因为频繁的写入GC日志,可能会导致GC停顿达到10数秒,而主要原因是GC日志的write方法效率较低,可通过采用GC日志写入固态硬盘减少GC停顿。

在GC停顿中还需要知道一个安全点以及安全区域的概念,及线程需要到达安全点或者到达安全区域(为阻塞休眠线程准备,因为线程无法工作,无法知道现在是否到达安全点,用于主动式中断),才能执行GC停顿。

中断也采用两种方法,分别是抢先式中断以及主动式中断

抢先式中断:将所有线程中断,若发现线程未到达安全点再将其重新恢复,等待其到达安全点。

主动式中断:在安全点设置一个中断标志,线程轮询该标志,但标志置为True时,线程主动中断挂起。


垃圾收集器

并行:多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

并发:指用户线程与垃圾收集线程同时执行(但不一定并行,可能会交替执行),用户程序在继续运行,但垃圾收集程序运行与另一个CPU上。

1、Serial收集器-单线程-新生代

最基本,最老的收集器,简单的“Stop-the-world”中断所有进程

2、Parnew收集器-多线程-新生代-并行

单CPU、甚至双CPU不如Serial、因为线程开销,适用于CPU逻辑核心大于4的,效果比较明显。

3、Parallel Scavenge收集器-多线程-新生代-吞吐量

不同与ParNew,Parallel Scavenge关注于达到一个可控的吞吐量,又名“吞吐量收集器”(吞吐量=运行用户代码时间/运行用户代码时间+垃圾收集时间)。

要注意的是一昧的缩短GC停顿时间也是不行的,因为缩短GC停顿时间是以牺牲吞吐量和新生代空间来换取的,若原来GC停顿时间为10nm,需要执行3次,但缩短为7nm后可能需要5次。

4、Serial Old收集器-单线程-老年代

使用“标记-整理”算法,经常搭配CMS,作为CMS后备预案,失败后调用。

5、Parallel Old收集器-多线程-老年代

使用“标记-整理”算法

6、CMS(Concurrent Mark Sweep)收集器-多线程并发-老年代

目标:最短回收停顿时间

基于“标记-清除”算法

分四个步骤执行:初始标记 -> 并发标记 -> 重新标记 -> 并发清除(红色的即需要STW)

初始标记用于GC Roots直接能关联到的对象,并发标记在于GC Roots Tracing,重新标记是在并发后对那些线程重新修改过引用的对象重新标记,最终并发清除。注意的是若在并发清除过程中再次发生改变,则需要等到下一次的收集。

7、G1(Garbage First)收集器-多线程并行并发-新老年代

特点:1、并行与并发

2、分代收集(无需其他收集器配合)

3、空间整合(整体看是基于“标记-整理”,在局部(Region)是复制算法)

4、可预测的停顿(可明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒)

G1收集器将Java堆分为多个大小相等的独立区域(Region),并根据Region回收的价值(回收获得的内存以及需要耗费的时间)维护一个优先列表,从而能够预测时间。

G1收集器在面对老年代对新生代的引用上引入了一个Remember Set,但互相引用时在Remember Set(每个Region各有一个)中计入,从而避免需要全堆检索。

G1收集器执行步骤:初始标记->并发标记->最终标记->筛选标记(红色的即需要STW)

前两步与CMS类似,第三步不同的是将变化写入Remember Set log中,再合入Remember Set,最后一步的筛选回收为做到可控而选择不并发。


Minor GC 新生代GC  执行比较频繁

Major GC/Full GC 老年代GC

对象直接进入老年代的情况:①大对象,Survivor空间不足。②动态年龄判断,Survivor相同年龄对象大于一半,超过这个年龄的直接进入老年代。③空间担保分配,在Minor GC分配可能冒险的情况下,对象移入老年代。

KAI Java