垃圾对象分析:
- 引用计数法:
- 每个对象添加一个引用计数器,每次被引用,计数器+1,失去引用,计数器-1,计数器为0,就应该对象可以被回收了;
- 没法解决循环引用的问题;
- 可达性分析法:
- 从GC Root节点开始,遍历整条引用链,寻找存活节点,回收剩余的对象;
- 可以作为GC Root节点的对象有:
- 虚拟机栈中的reference对象;
- 本地方法栈中的引用对象;
- 方法区中静态变量的引用对象
- 方法区中常量的引用对象
引用的分类:
- 强引用:
- 只要强引用存在,就不会回收;
- 软引用
- 内存不足时,会回收软引用;
- 弱引用
- 下一次GC时,就会被回收;
- 虚引用
- 即时失效;
垃圾回收算法:
- 标记清除算法
- 主要分为两步:
- 第一步,标记出所有需要清除的对象;
- 第二步,统一回收所有标记的对象;
- 缺点:
- 标记与清除的效率都比较低;
- 标记清除过后会产生大量不连续的内存碎片,当大对象需要分配内存时,可能又会触发一次GC;
- 主要分为两步:
- 复制算法
- 将内存一分为二,先使用其中一块,其中一块用完了,再将存活的对象找出来,复制到另外一块,然后清理掉之前一块;
- 新生代大多采用复制算法来实现;
- 新生代被分为Eden区和Survior区,其中Survior区又被分为From区和To区,其比例默认是Eden:From:To = 8 :1 :1
- 每次先使用Eden区,Eden满了,复制到From区,然后清空Eden区,From区也满了,将Eden区与From区的存活对象复制到To区,然后清空Eden区与From区,再将From区与To区互换,一直保持To区是空的;
- 标记整理算法
- 标记整理算法多用于老年代,就是在标记清除的基础上加上内存整理;
- 分代算法
- 根据对象的声明周期将内存划为多块,JAVA将内存划为新生代与老年代;
- 新生代与老年代的比例默认是 1 :2
- 新生代发生的是monitorGC,老年代发生的FullGC;
- 每次monitorGC,新生代对象年龄+1,年龄达到阀值就会进入老年代;
- MonitorGC发生前会先进行分配担保,如果分配担保失败就会进行FullGC;
- 分配担保是为了保证monitorGC的安全性,会去判断老年代的最大可用空间是否大于新生代的使用空间,如果小于,就意味着这次有可能会发生FullGC;
GC优劣标准:
- 吞吐量:GC线程会与应用线程竞争CPU时间,吞吐量指的是应用线程使用CPU时间的比例,吞吐量越高越好;
- 停顿时间:GC程序为了保证在回收过程中不会产生新的垃圾,达到一定的一致性,会在GC时让应用线程停顿一段时间,停顿时间越短越好;
垃圾回收器:
- Serial收集器
- 新生代、单线程、串行收集器,采用复制算法,会在GC时Safepoint暂停所有用户线程;
- ParNew收集器
- Serial的多线程版本,单核CPU表现不如Serial;
- Parallel Scavenge收集器
- 追求最大吞吐量,和ParNew一样是多线程、复制算法收集器;
- 停顿时间短适合用户交互类的程序;
- 吞吐量大适合后台运算不需要太多交互的程序;
- 停顿时间、新生代空间、吞吐量三者不能同时满足;
- Serial Old收集器
- Serial收集器的老年代版本,采用标记整理算法;
- Parallel Old收集器
- 老年代、多线程、标记整理;
- CMS收集器
- 以最短停顿时间为目标;
- 采用标记清除算法;
- 步骤:
- 初始标记:仅标记出于GCRoot节点直接关联的节点,速度很快,需要STW;
- 并发标记:对GCRoot整条链路进行标记,时间最久,不需要STW;
- 重新标记:修正并发标记期间,用户程序的修改导致的不一致的对象,时间比初始标记略久,需要STW;
- 并发清除:不需要STW;
- 缺点:
- CPU资源敏感:由于CMS是并发与用户线程一起工作,所以在CPU核数不足时,吞吐量会变的非常低;
- 无法清理浮动垃圾:在并发清除时不会STW,用户线程还会持续产生垃圾;
- 会产生内存碎片:CMS是标记清除算法,没有整理的过程;
- G1收集器
- 将JAVA堆分为对多个region;
- 新生代与老年代不再物理隔离,而是散落在不同的region上;
- 可预测的时间模型:G1根据历史回收时间在每个region上加上了价值权值,可以根据权值来优先回收价值最大的region;
- 避免全堆扫描:每个region维护了一个Remembered Set,记录了与Reference数据的关系,可达性分析时,合并Remembered Set,就可以遍历出GCRoots的整条链路;
- 步骤:
- 初始标记;
- 并发标记;
- 最终标记:合并并发标记阶段的Remembered Set log到Remembered Set中;
- 筛选回收:根据Region中的价值权值进行排序,根据用户期望停顿时间来选择回收部分Region;
基础概念:
- SafePoint:
- GC线程工作的过程中,由于用户线程也在工作,可能会导致引用关系的变化,所以GC发生时需要在某一个点,让所有的用户线程都停下来;
- 停下的方式有两种:
- 抢先式中断:GC发生时,立即中断所有用户线程,再去检查线程是否在SafePoint上,如果不在,就继续运行到SafePoint再中断;(这种方式很少有虚拟机用)
- 主动式中断:GC程序会在SafePoint设置标识,用户线程读到标识为真时,就把自己中断挂起;
- 可作为SafePoint的点:
- 循环体的结尾;
- 方法返回前;
- 调用call方法后;
- 抛出异常的地方;
- Safe Region:
- 用户线程如果没有CPU时间片,就没法跑到SafePoint,GC程序这种线程所在的代码标记为Safe Region,线程想进入到RUNNING态,就要先检查GC是否结束,没结束的话,就不能从Safe Region出去;