一. 如何找到一个垃圾?
java
1) 引用计数算法:给对象添加一个引用计数器,有一次引用,计数器值就加1;当引用失效时,计数器值就减1。不少流程的编程语言例如Python都使用这种方法管理内存,可是主流的Java虚拟机没有选用它,主要缘由是它很难解决对象之间相互循环引用的问题。面试
2) 根可达性分析算法:由于引用计数算法没法解决对象之间相互循环引用的问题,继而引出了这个算法。思想是以GC Roots做为起始点开始向下搜索,所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链时,则这个对象是不可用的,就是垃圾。如图所示:算法
二. 找到一个垃圾后,如何清除它?编程
1) Mark-Sweep(标记清除):先标记出可回收的对象,而后清除。以下黑色部分为可回收对象,灰色部分为存活对象,绿色部分为未使用对象。服务器
它的主要不足有两个:
微信
1. 标记和清除两个过程的效率都不高
并发
2. 另外一个是空间的问题,标记清除以后产生了大量不连续的内存碎片,碎片会致使之后须要分配较大对象时,没法找到足够的连续内存,继而提早触发另外一次垃圾收集动做。app
2) Copying(拷贝):将内存划分为两块,将存活对象所有copy到下面的区域,而后把上面的所有清除。编程语言
新生代中的Survivor1和Survivor2就是这样的。学习
缺点:浪费内存
3) Mark-Compact(标记压缩或者标记整理):既不想碎片化,又不想浪费内存,就先将回收的对象标记起来,而后一边回收,一边把存活对象向一端移动。
缺点:效率偏低
三. 内存分配和回收的过程是什么样子的
内存分配与回收策略以下图:
名词解释:
YGC ==Young GC == Minor GC 新生代回收
Major GC 老年代回收
Full GC 新生代和老年代一块回收
图示解释:
1) new出一个对象,首先尝试在栈上分配,若是能直接分配下,就在栈上分配。对象在栈里一旦pop,对象就没了。(栈上分配好处:不须要GC介入,对象用完就能够消失,可是栈的空间很是小)
2) 若是栈上分配不下,而且对象比较大,会直接进入老年代,经历Major GC/Full GC的回收,而后消亡。
3) 若是对象不够大,先在线程本地分配 (TLAB: Thread Local Allocation Buffer),若是TLAB满了,直接进入Eden,无论进不进入TLAB,最终都会进入Eden区域,进入Eden区域会竞争资源,出现线程同步,特别消耗资源,所以Hotspot作了优化,Eden区域为每一个线程都分配了一小块区域,这样就不会每一个线程都去抢夺资源。
4) 在Eden区域,对象若是能被GC清除,直接就消亡了
5) 在Eden区域,若是回收不掉,进入Survivor1区域,而后再次回收,年龄加1,若是年龄到了(CMS默认是6岁,其余默认都是15岁),而后进入老年代,若是年龄没到,进入Survivor2区域,而后再次回收,循环往复,Survivor1和Survivor2还会来回替换(由于有个copy垃圾回收算法),直到进入老年代。
四. 垃圾收集器(10种)
前面六个是分代模型,后面四个是不分代模型。
1. 垃圾收集器的发展路线,是随着内存愈来愈大的过程而演进的。
从分代算法演化到不分代算法
Serial算法 几十兆
Parallel算法 几个G
CMS 几十个G 承上启下,开始并发回收
-三色标记-
2. JDK诞生,Serial追随 提升效率,诞生了PS,为了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,可是CMS毛病较多,并发垃圾回收的缘由是由于没法忍受Serial回收的STW。
3. Serial 收集器 年轻代 串行回收
特色:在回收垃圾时,必须暂停其余全部的工做线程,知道它收集结束
4. PS 年轻代 并行回收
5. ParNew 年轻代 配合CMS的并行回收,它和Parallel Scavenge同样,区别就是它配合CMS使用。
6. Serial Old 收集器
7. Parallel Old
8. CMS 全称 ConcurrentMarkSweep 老年代 并发的,垃圾回收和应用程序同时进行,下降STW的时间(200ms),CMS问题不少,因此没有一个版本默认是CMS,只能手动设定。CMS既然是MarkSweep,就必定会有碎片化的问题,碎片达到必定的程度,CMS老年代分配对象分配不下的时候,使用Serial Old进行老年代回收。(面试重灾区)
主要有四个过程:
初始标记、并发标记、从新标记、并发清理
CMS的缺点是:产生了浮动垃圾,而且使用Serial Old来清理整个老年代,这是CMS设计的缺陷;可是若是CMS作好调优,支持的内存要比Parallel Old大的多。
想象一下:
PS + PO -> 加内存 换垃圾回收器 -> PN + CMS + Serial Old (几个小时-几天的STW) ,几十个G 的内存,单线程的回收 -> G1 + Full GC 几十个G -> 上T内存的服务器 ZGC
CMS并发标记采用的是: 三色标记法 + Incremental Update
9. G1 垃圾回收器 (200ms - 10 ms)
算法:三色标记 + SATB
因为愈来愈多的内存须要回收,必然会产生STW,因此G1应运而生。
逻辑分代,物理不分代。
10. ZGC (10ms - 1ms ) PK C++
算法:ColoredPointers + LoadBarrier
11. Shenandoah
算法:ColorPointers + WriteBarrier
CMS中新生代的默认年龄是6,PS/PO中新生代的默认年龄是15,进入老年代,能够经过参数:-XX:MaxTenuringThreshold配置
jdk1.0自带的Serial和Serial Old,如今用的最多的是Parallel Scavenge和Parallel Old,调优用的是ParNew和CMS,jdk1.8用G1也没有问题
上图:前面六种,通常都是新生代和老年代配合使用。ParNew和CMS,Serial和Serial Old,Parallel Scavenge和Parallel Old。
jdk1.8默认是Parallel Scavenge和Parallel Old,不论是单线程Serial仍是并行Parallel,只要人多,都会出现问题,因此就有了承前启后的ParNew和CMS。
本文分享自微信公众号 - Java学习进阶手册(javastudyup)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。