目录
串行垃圾回收器(Serial Collector)
并发式垃圾回收器
7种经典垃圾回收器
Serial 收集器
ParNew收集器
Parallel Scavenge收集器
CMS收集器
G1收集器
小结
常用参数
按垃圾回收器的线程数可以分为串行垃圾回收器和并行垃圾回收器。
串行垃圾回收器(Serial Collector)
串行回收指在同一时间段内只有一个CPU用于执行垃圾回收操作,这时候工作线程被暂停,直到垃圾收集工作结束。串行垃圾回收器常常被用在单CPU处理器或内存比较小的硬件平台。
并行垃圾回收器(Parallel Collector)
并行回收是指在同一段时间内有多个CPU同时用于垃圾回收,虽然提高了吞吐量,但是还是存在“Stop The World”的问题。
按工作模式可以分为并发式垃圾回收器和独占式垃圾回收器。
并发式垃圾回收器
并发式垃圾回收器可以与应用程序交替工作,可以尽量减少程序的卡顿时间。
独占式垃圾回收器
独占式垃圾回收器(Stop The World)一旦执行,就停止程序中的所有用户线程,直到垃圾回收工作结束后用户线程才恢复工作。
7种经典垃圾回收器
Serial 收集器
Serial串行收集器,适合于单核CPU的客户端端,是HotSpot最早退出的一款垃圾收集器。一般用于嵌入式,单核CPU的场景,一般搭配Serial Old 一起使用,但是这款垃圾收集器在现代的多CPU服务器环境下用得非常少了。
ParNew收集器
ParNew收集器与Serial收集器非常像,Serial收集器是单线程的,ParNew是多线程的。Par是Paraller的缩写,New表示只能处理新生代。ParNew收集器在新生代中也是采用复制算法,同样存在“Stop The World”的问题。
ParNew更适合在多CPU的服务器,Serial更适合用于单CPU的服务器(如嵌入式,client客户端等)。ParNew在多CPU环境下能够充分利用多CPU的物理硬件优势快速完成垃圾收集,但是在单CPU环境下存在线程切换的额外开销,导致效率不一定比Serial快。而Serial虽然能够部署在多CPU服务器,但是没办法充分利用CPU核数优势,只是使用单核资源,比不上ParNew高效。
在JDK9之后,老年代不再搭配Serial Old。
ParNew 收集器在jdk8及之后用得非常少,jdk9已经不再推荐使用。
Parallel Scavenge收集器
Parallel Scavenge收集器同样采用了复制算法,并行收集,也存在“Stop The World"问题。与ParNew收集器不同,Parallel Scavenge设计出来是吞吐量优先的垃圾收集器。
高吞吐量可以更高效地利用CPU时间,尽快完成任务,主要用于后台运行而不需要太多交互的任务。比如批量处理,订单处理,后台定时任务等场景。
jdk6之后提供了Parallel Old收集器用于替换serial old收集器,以便老年代的垃圾收集提高效率。因此一般都是Parallel Scavenge和Parallel Old搭配使用,jdk8默认的就是这种垃圾收集器。
CMS收集器
是在JDK1.5,HotSpot虚拟机推出的一款在强交互应用中具有划时代意义的垃圾收集器,CMS(Concurrent-Mark-Sweep)收集器,是HotSpot虚拟机中第一款真正意义上的并发收集器,第一次实现了垃圾收集线程与用户线程同时工作。
CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。用户线程停顿时间越短,就越适合于与用户交互的程序。目前主要适合于互联网网站或一些B/S系统的服务端,适合于比较重视服务响应速度,希望系统卡顿时间短的应用。
jdk9之后已经废弃了CMS收集器,默认的收集器已经改为了G1收集器。在jdk14已经删除了CMS收集器。
CMS垃圾收集器采用标记-清除算法,也会存在“Stop The World”的问题。
CMS工作原理,主要分为以下4个阶段:
1.初始标记:在这个阶段,所有的工作线程都会因为“Stop The World”停止,然后标记出GC Roots 能够关联的对象,一旦标记完成,所有的被暂停的工作线程又恢复工作,这个过程非常快。
2.并发标记:把GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程比较耗时但是无需停止工作线程。
3.重新标记:这个过程是为了修复并发标记阶段中,因用户线程继续工作而导致标记对象产生变动导致在GC Roots中变为不可达或可达的对象,只保留可达对象。也存在“Stop The World”的情况,这个过程比初始标记的耗时要多,但是比并发标记的耗时少。
4.并发清除:清理删除标记阶段判断的已经死亡的对象,释放内存。
特点:
1.由于并发标记和并发清除阶段,无需“Stop The World”,所以整体回收是低停顿的。
2.由于在垃圾收集阶段用户线程是没有中断的,在CMS回收过程中,还应该确保用户线程又足够的内存空间,要是没有足够的内存空间,CMS垃圾收集器就会临时启用Serial Old收集器来重新进行老年代的垃圾收集,导致卡顿时间边变长。
3.由于采用了标记-清除算法,导致内存回收后所释放的内存空间存在不连续的情况。
4.为什么不采用标记-压缩算法?是因为在标记阶段无需停止用户线程,就不能移动对象在内存中的位置,采用压缩算法是需要把不可达对象删除,在内存中把可达对象移动到相对连续的位置,然后留下较大的内存空间。
G1收集器
官方推出G1(Garbage First)收集器的目标是在延迟可控的情况下获得尽可能高的吞吐量(即在有限的暂停时间内获得最大的吞吐量)。 G1收集器把内存分割为多个不相关的区域(region),使用不同的region表示Eden,survior0,survior1,老年代等,;G1中的新生代老年代这些堆内存物理上是不连续的逻辑上是连续的。避免在整个java堆中进去全区域的垃圾收集,G1与过去的Parallel,CMS等收集器的内存模型差别非常大,是按region区进行GC Roots检索,每次垃圾收集时都是检查部分的region,这样的耗时就比较小。旧的垃圾收集器是当新生代或老年代快满的时候才进行垃圾收集,是对整个新生代或老年代堆进行GC Roots可达性检索非常耗时。
region区:region分为Eden,Survior,Old,Humongous内存区域,每一个region就是这些内存区域的一个角色,Humongous区域比较特殊,是G1用来存放大对象的内存区域,当大对象超过1.5个region时,就会放到Humongous区。
G1收集器适合在具有大内存多处理器的机器上的程序,非常适合要求GC停顿时间少的场景,但是内存一般在6G以下的表现甚至不如CMS收集器,当内存大于等于6G时表现就比较好。
G1收集器是jdk7推出的,但是在jdk9才是默认的垃圾收集器。
小结
最小化使用内存和并行开销:Serial GC
最大化程序吞吐量:Parallel Scavenge GC
最小化GC停顿时间:CMS GC
JKD9之后用G1(ZGC还未成熟,等到官方默认使用ZGC的jdk版本之后再使用ZGC吧)
常用参数
-XX:+UseSerialGC 开启Serial收集器,新生代为Serial,老年代为Serial Old。
-XX:+UseParNewGC 开启新生代使用ParNew收集器
-XX:ParallelGCThreads ParNew收集器线程数量,默认是与CPU核数相同
-XX:+UseParallelGC 开启新生代使用Parallel Scavenge收集器,jdk8默认开启。
-XX:+UseParallelOldGC 开启老年代使用Parallel Old收集器,jdk8默认开启。
-XX:+UseParallelGC 和 -XX:+UseParallelOldGC 任意一个开启后,另外一个默认也开启。
-XX:MaxGCPauseMillis 设置Parallel Scavenge收集器的最大停顿时间(Stop The World)
-XX:GCTimeRatio 设置Parallel Scavenge收集器的垃圾收集时间占总时间的比例,默认值是99,也就是垃圾收集时间不超过1%,与 -XX:MaxGCPauseMillis 有一定的矛盾,暂停时间越长, -XX:GCTimeRatio 就越有可能超过限制。
-XX:+UseConcMarkSweepGC 开启使用CMS垃圾回收器,开启这个参数后会自动开启-XX:+UseParNewGC,即ParaNew(新生代) + CMS(老年代) +Serial Old(老年代备用)的组合。
-XX:CMSlnitiatingOccupanyFraction 表示CMS垃圾收集器的堆内存使用率阈值,一旦达到该阈值就开始回收,在jdk5时默认是68,表示堆内存使用率达到68%时会执行一次CMS回收,jkd6之后是92。如果内存增长缓慢,可以设置一个较大的阈值,较大的阈值能够减少CMS的执行频率,减少老年代的回收次数从而较为明显的改善程序性能。如果内存增长非常快,在要降低阈值,避免频繁触发老年代串行收集器。
-XX:useG1GC 开启G1垃圾收集器
-XX:G1HeapRegionSize 设置G1收集器的每个Region大小,值是2的幂,范围是1M-32M之间,默认是堆的1/2000。
-XX:MaxGCPauseMillis 设置G1收集器最大的停顿时间,默认是200ms。