相关概念
并行与并发
- 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
- 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行。而垃圾收集程序运行在另一个CPU上。
吞吐量(Throughput)
吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即
吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)
假设虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
垃圾回收算法
引用计数( Reference Counting)
古老的回收算法,原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象,此算法最致命的是无法处理循环引用的问题
复制(Copying, 新生代常用)
此算法把内存空间划分为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。此算法毎次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。
标记-清除(Mark- Sweep)
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片(未使用的空间不连续)。
标记-整理(Mark- Compact, 老年代常用)
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,清除未标记对象井且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避兔了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
垃圾收集器(垃圾回收算法具体实现)
垃圾收集机制(hotspot为例)
Minor GC
- 当年轻代堆空间紧张时会被触发
- 相对于Full GC而言,收集间隔较短
新生代GC( Minor GC): Minor GC指发生在新生代的GC,因为新生代的Java对象大多都是朝生夕死,所以 Minor GC非常频繁,一般回收速度也比较快,当Eden空间不足以为对象分配内存时,会触发 Minor GC。
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发 Minor GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到 Survivor区。然后整理 Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影向到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
Full GC / Major GC
- 当老年代或者持久代堆空间满了,会触发全收集操作
- 可以使用 System.gc0方法来显式的启动全收集
- 全收集一般根据堆大小的不同,需要的时间不尽相同,但一般会比较长。不过,如果全收集时间超过3到5秒钟,那就太长了。
老年代GC( Full GC/Major GC):Full GC指发生在老年代的GC,出现了 Full GC一般会伴随着至少ー次的 Minor GC(老年代的对象大部分是 Minor GC过程中从新生代进入老年代),比如:分配担保失败。Full GC的速度一般会比 Minor GC慢10倍以上。当老年代内存不足或者显式调用 System.gc()方法时,会触发 Full GC
新生代收集器
串行收集器(Serial)
Serial收集器是 HotSpot运行在 Client模式下的认新生代收集器,它的特点是只用一个CPU/一条收集线程去完成GC工作,且在进行垃圾收集时必须暂停其他所有的工作线程(“ Stop The World”-后面简称STW)可以使用-XX+ UseSerilalGC打开。虽然是单线程收集,但它却简单而高效,在VM管理内存不大的情况下收集几十M~一两百M的新生代,停顿时间完全可以控制在几十毫秒到一百多毫秒内
并行收集器(ParNew)
Pardew收集器其实是前面 Serial的多线程版本,除使用多条线程进行GC外,包括 Serial可用的所有控制参数、收集算法、STW、对象分配规则、回收策略等都与 Serial完全一样(也是VM启用CMS收集器-XX:+UseConcMarkSweepGC的默认新生代收集器)
由于存在线程切换的开销,ParNev在单CPU的环境中比不上 Serial,且在通过超线程技术实现的两个CPU的环境中也不能100%保证能超越Serial,但随着可用的CPU数量的增加,收集效率肯定也会大大增加 (ParNew收集线程数与CPU的数量相同,因此在CPU数量过大的环境中,可用-XX: ParallelGCThreads=<N>参数控制GC线程数)。
Parallel Scavenge收集器(jdk1.8默认收集器)
与 Pardew类似,Parallel Scavenge也是使用复制算法,也是并行多线程收集器.但与其他收集器关注尽可能缩短垃圾收集时间不同, Parallel Scavenge更关注系统吞吐量,系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)停顿时间越短就越适用于用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则适用于后台运算而不需要太多交互的任务-可以最高效率地利用CPU时间尽快地完成程序的运算任务, Parallel Scavenge提供了如下参数设置系统吞吐量:
老年代收集器
Serial Old收集器
Serial Old是Serial收集器的老年代版本,同样是单线程收集器,使用“标记-整理”算法
Parallel Old收集器
Parallel Old是Parallel Scavenage收集器老年代版本,使用多线程和“标记-整理”算法,吞吐量优先,主要与Parallel Scavenge配合在注重吞吐量及CPU资源敏感系统内使用
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一款具有划时代意义的收集器,一款真正意义上的并发收集器,虽然现在已经有了理论意义上更好的GI收集器,但现在主流互联网企业线上选用的仍是CMS(如淘宝、微店)
CMS是一种以获取最短回收停顿时间为目标的收集器(CMS又称多并发低暂停的收集器),基于“标记-清除”算法实现,整个GC过程分为以下4个步骤:
- 初始标记(CMS initial mark)
- 并发标记(CMS concurrent mark:GC Roots Tracing 过程)
- 重新标记(CMS remark)
- 并发清除(CMS concurrent sweep:已死对象将会就地释放,注意:此处没有压缩)
其中步骤(初始标记、重新标记)仍需STW,但初始标记仅只标记了一下 GC Roots能直接相关联到的对象,速度很快,而重新标记则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,虽然一般比初始标记阶段稍长,但要远小于并发标记时间
分区收集-G1收集器
G1(Garbage-First)是一款面向服务器端应用的收集器,主要目标用于用于用于用于用于用于用于用于用于用于用于用于用于收集器收集器收集器收集器收集器用于用于用于收集器收集器