Java GC过程笔记

本文参考自:马士兵老师JVM基础入门丨GC基础知识丨GC算法丨JVM的垃圾回收器丨

只看了马老师关于GC这部分的讲解,后面的调优没有看,然后做一个基本的关于GC过程的笔记。
本文分成如下几个部分:
对象的生命周期
GC的主要的算法概述

对象的生命周期
马士兵老师这张图总结得太牛逼了,我直接拿过来做了点笔记:

JVM首先会看一下这个对象是不是比较大的对象,如果不是比较大的对象可以直接分配在虚拟机栈中,在方法调用完成之后会直接弹出,不需要GC的介入。如果这个对象特别大,也不会直接分配在堆上的Eden区,会直接直接进入老年代。如果对象的内存中等大,考虑首先分配在TLAB,也就是ThreadLocalAllocationBuffer上。设计TLAB这一块是为了避免线程需要同步内存分配的问题。
TLAB也在Eden,所以其实也是分配在了Eden。后续的过程就会通过GC。
如果需要GC,首先会被移动到第一个Survivor,之后如果还是存活的话会在第一个和第二个Survivor之间反复横跳,直到达到了分代年龄,就会被放到老年代。
GC算法概述
堆的分代年龄模型
堆上的内容的划分是根据【代】来划分了,也就是分代年龄。

GC算法主要分成三种:
1. Mark-Sweep
Mark-Sweep算法类似于递归的过程,将可达的目标标记,回收没有标记的对象。算法本身比较简单,但是会造成碎片化的问题。
2. Mark-compact
Mark-Compat和MS类似,但是清除垃圾之后也会整理内存碎片。缺点是效率比较低。
3. copying算法
在MS算法中,如果死亡的对象比较多,在清除过程中还需要扫描死亡的对象,这样就比较耗时。使用copying算法的话,只需要将存活的对象从一个内存部分复制到另外的一个内存部分即可。但是这个方法本身也需要开辟另外的一部分内存区域用于存放对象。
在堆中,年轻代使用的是copying算法,在老年代使用的是MS或者是MC算法。
GC中实际采用的算法是上面的算法的组合:

最原始的算法:
Serial & Serial Old:适用于内存比较小的情况。Serial是一个STW的算法,在GC线程执行的过程中,工作线程会被暂停。并且这类的算法是使用一个线程串行执行的。Serial Old就是使用MS或者是MC算法执行GC,主要是针对老年代。
Parallel Scanvenge& Parallel Old:是一种并行的算法,主要使用copying算法;然后Parallel Old是在多线程上使用MC的GC算法。
在上述的算法中,并不是使用越多线程越高效,而且上述的GC算法都是STW的算法。所以现代的GC使用了名为CMS的算法:
Concurrent Mark Sweep。
这是一种并发的算法,主要分成四个部分:
初始标记:首先标记GC Root,这个过程是STW的
使用多个线程并发的方式标记GC Root引用链上的其他的节点,这里不需要STW
重新标记:上面一步中没有使用STW,在这段过程中有些对象可能会被重新引用到,所以又需要重新标记,避免错误
并发清理:标记完成的对象清除掉
