如何判断一个对象存活?
- 引用计数。对象每被引用一次,计数器值+1.为0时代表对象可回收。缺点:对象相互引用,无法回收
- 可达性分析:从一系列的 GCRoots 开始一个引用链。一个对象无法连接到引用链则可回收(最多二次标记)。
- GCROOts:
- 虚拟机栈中所引用对象,正在执行的方法中的局部变量、方法参数所引用对象。
- 本地方法栈中所引用对象
- 方法区中类静态变量所引用对象
- 方法区中常量池所引用对象,比如说字符串常量池
- 最多二次标记:如果重写了finalize()方法并且是第一次调用则会加入到一个F队列中,等待执行并第二次标记。否则直接标记为可回收。
多种引用?
- 强引用:普通的对象引用关系,不会被回收。
- 软引用:在内存不足时,才回收
- 弱引用:只能存活到下一次垃圾回收之前
- 虚引用:用于监控对象的垃圾回收过程。
垃圾回收算法:
- 标记清除:标记对象可回收,然后根据标记回收可回收对象。缺点:效率不稳定,且会造成空间碎片。可能会因为大对象分配不到内存导致一次GC
- 标记复制:将内存分为eden区和两个小区,比例是8:1:1 . 将存活对象移动到一个小区域。在剩下的9中回收。特点:如果不想浪费50%就要空间担保。不适合对象存活率高的场景。适合于新生代。
- 标记整理:将所有存活的对象移动到一段,根据分界线进行回收。
- 标记清除和标记整理区别:
- 前者不会移动对象,后者会移动对象。
- 移动对象,需要更新所有引用这些对象的地方,会导致STW
- 不移动对象,会导致空间碎片问题。需要列表记录,导致内存分配困难
- 怎么选择:CMS在前期使用标记清除,内存分配不了时使用整理算法。
并发标记-三色标记
是什么:并发指用户线程和垃圾回收线程同时都运行。三色标记指对象根据状态有三种颜色。从GCroot开始为黑色,之后灰色对象是指和黑色对象连接的且还和白色对象连接,此白色对象之后变灰,之前灰变黑。最后还是白色对象代表没有和引用链连接。
有什么问题:因为用户线程还在执行,所以如果满足了两点:1. 删除一条灰色到白色的连接。2. 新增一条黑色到白色的连接。此时,会误将该对象回收。
解决方法:
- 增量更新:记录下从黑色节点新增到白色节点。之后从该黑色节点再来一次。简单理解也就是让该节点重新变灰,就可以让误杀节点保存。
- 原始快照:记录下删除灰色到白色的连接。也就是简单理解,保存刚开始的状态,没有删除之前。
垃圾回收器
- Serial 单线程、适用于新生代,使用标记-复制算法,最大问题就是会stw
- ParNew serial的多线程版本,新生代
- Parallel Scavenge :并发的多线程版本,新生代、提供一个高吞吐量。
- serial old:老年代,标记整理
- parallel old :老年代。标记整理
- CMS:用于老年代,以获取最短停顿时间为目标。流程:初始标记–并发标记–再次标记–并发清除。收集结束会产生空间碎片。
- G1:标记整理实现。获取一定停顿时间内最优垃圾回收。流程:初始标记-并发标记-最终标记-筛选回收。作于于整堆,将整堆分为了很多个region区域。G1跟踪每个区域大小,后台维护一个优先级列表。每次根据可回收的停顿时间,回收优先级高的region区域
CMS详细回收过程?问题?
CMS目标是最短的停顿时间。所以使用了并发的标记、清除
分为四步:
- 初始标记:标记于gcroots直接相连的对象。量少速度快。
- 并发标记:采用三色标记来进行并发的标记,此时用户程序依旧可以运行。
- 重新标记:在第二阶段用户程序依旧可以运行,就可能出现误删了不该回收的对象。只有同时满足这两个条件的才可能出现:1.删除了灰色节点到白色节点的连接。2. 新增了黑色节点到白色节点的连接。解决:CMS采用的是增量更新的解决方法,破坏条件2.新增连接的黑色节点将被记录,之后把这个节点当作灰色节点来重新标记。
- 并发清除:清除标记为可回收的对象
问题:
- 并发回收导致CPU资源紧张。CMS默认启动的回收线程是 (cpu核心数 + 3)/4
当cpu数小于4个的时候。会对用户程序造成影响。 - 无法清理浮动垃圾:并发阶段,用户程序依旧运行,还会产生垃圾。CMS标记结束就无法解决这些垃圾。
- 并发失败:用户程序运行,老年代还会有对象进入。所以不能等老年代满了才进行垃圾回收。必须预留一部分内存,一般默认是老年代内存占用92%就会触发GC。但是此时预留下来的空间不足以分配新入老年代的对象。则会出现并发失败。需要临时启用serial old 来进行垃圾回收,会STW。
- 内存碎片问题:清除期结束会有内存碎片产生,内存不够分配会触发Full GC。但是也有一个参数默认是开启的,就是启用了整理。移动存活对象。还有一个参数是经过几次FullGC后才会整理。默认是0,每一个FUllGC都会整理。
G1 的回收过程:
G1从整体上来看是基于标记-整理算法。但是对于两个region直接则是标记-复制
- 初始标记:标记与Gc roots 直接连接的对象
- 并发标记:三色标记法
- 最终标记:解决并发标记带来的问题。采用原始快照解决。
- 清除:更新Region的统计数据,对各个Region进行价值和回收成本排序。按照用户期望停顿时间进行选择region回收。将旧region中的存活对象复制到新region,回收整个旧region。
对象分配:
- 对象优先在Eden区分配。举例说明:堆一共20MB。新生代和老年代一半一半。新生代,eden区和sur区8:1:1.连续分配三个2MB对象。第四次分配一个4MB对象。会触发Mioner GC。Eden区不够,触发GC。发现6MB都不可回收,但是sur区也放不下,就会触发空间担保,占用老年代内存。之后再Eden区分配4MB。
- 大对象直接进入老年代。
- 对象年龄判定:熬过的GC超过15此就会进入老年代。
- 动态年龄判断:如果sur区中低于或等于某年龄的对象总和大于sur区内存的一半。则大于或等于该年龄的对象进入老年代。
空间分配担保机制
进行minor GC之前必须线判断老年代可用空间是否大于此时新生代所有对象大小。大于则可以安全的将存活对象复制到sur区。否则进行判断是否开启了空间担保机制,开启了就判断老年代可用空间是否大于之前历次升级为老年代对象平均大小。大于则冒险minor Gc。小于则Full GC。