1.JVM垃圾回收器
所谓垃圾回收方式,是指JVM提供的几种不同的垃圾回收器,不同的垃圾回收器进行垃圾回收时采用不同的方式。当然,总体原则遵循java垃圾回收机制。每种方式都有自己的优势与劣势。我们编程的时候可以通过向JVM传递参数来选择垃圾回收器。不同的垃圾回收器有很大的不同,可以为我们提供完全不同的应用程序性能,所以理解每种垃圾回收器,并且根据不同的应用选择进行正确的选择是非常重要的。
垃圾回收器主要有4种:
串行垃圾回收器(Serial Garbage Collector)
并行垃圾回收器(Parallel Garbage Collector)
并发标记扫描垃圾回收器(CMS Garbage Collector)
G1垃圾回收器(G1 Garbage Collector)
上图是HotSpot里的收集器,中间的横线表示分代,有连线表示可以组合使用。
名词解释:
串行回收
gc单线程内存回收、会暂停所有用户线程并行回收
收集是指多个GC线程并行工作,但此时用户线程是暂停的;所以,Serial是串行的,Parallel收集器是并行的,而CMS收集器是并发的并发回收
是指用户线程与GC线程同时执行(不一定是并行,可能交替,但总体上是在同时执行的),不需要停顿用户线程(其实在CMS中用户线程还是需要停顿的,只是非常短,GC线程在另一个CPU上执行)
2.串行垃圾回收器(Serial Garbage Collector)
使用单线程处理所有垃圾回收工作,无需多线程交互,效率较高。但也无法使用多处理器的优势,在进行垃圾收集时,必须暂停所有其他工作线程,直到收集完成。
使用-XX:+UseSerialGC
来开启:
新生代、老年代使用串行回收
新生代复制算法
老年化标记-压缩算法
优势:最古老,最稳定,没有多线程交互开销更高效,是Client模式下默认的新生代收集器。
缺点:Stop-The-World(全暂停),可能会产生较长的停顿
适用场景:
单处理器机器
小数据量(100M左右)情况下的多处理器机器上
2.1 新生代Serial回收器
-XX:+UseSerialGC来开启
Serial New+ Serial Old的收集器组合进行内存回收使用复制算法。
独占式的垃圾回收。
一个线程进行GC,串行。其它工作线程暂停。
2.2 老年代Serial回收器
-XX:+UseSerialGC来开启
Serial New+ Serial Old的收集器组合进行内存回收使用标记-压缩算法
串行的、独占式的垃圾回收器。
因为内存比较大原因,回收比新生代慢
3.并行垃圾回收器(Parallel Garbage Collector)
对年轻代进行并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。如果年老代不使用并发收集的话,默认是使用单线程进行垃圾回收,因此会制约扩展能力。并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的 CPU 上,它产生的停顿时间要短于串行回收器,而在单 CPU 或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程的压力,它的实际表现很可能比串行回收器差。
使用-XX:+UseParNewGC
来开启:
新生代并行
老年代串行
Serial收集器新生代的并行版本,并行的、独占式的垃圾回收器
复制算法
多线程,需要多核支持
-XX:ParallelGCThreads 限制线程数量,可以设置与机器处理器数量相等
使用Parallel收集器
:
类似ParNew
新生代复制算法
老年代标记-压缩算法
更加关注吞吐量:
-XX:+UseParallelGC
使用Parallel收集器 + 老年代串行,Server
模式下的默认值-XX:+UseParallelOldGC
使用Parallel收集器 + 并行老年代
最大垃圾回收暂停:指定垃圾回收时的最长暂停时间,通过-XX:MaxGCPauseMillis指定。为毫秒.如果指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值。设定此值可能会减少应用的吞吐量。
吞吐量:吞吐量为垃圾回收时间与非垃圾回收时间的比值,通过-XX:GCTimeRatio来设定,公式为1/(1+N)。例如,-XX:GCTimeRatio=19时,表示5%的时间用于垃圾回收。默认情况为99,即1%的时间用于垃圾回收。
优势:关注CPU吞吐量,即运行用户代码的时间/总时间,比如:JVM运行100分钟,其中运 行用户代码99分钟,垃 圾收集1分钟,则吞吐量是99%,这种收集器能最高效率的利用CPU,适合运行后台运算
缺点:垃圾收集过程中应用响应时间可能加长
适用场景:对吞吐量有高要求,多CPU、对应用响应时间无要求的中、大型应用。如后台处理、科学计算。
4.并发标记扫描垃圾回收器(CMS Garbage Collector)
并发收集器主要减少年老代的暂停时间,它在应用不停止的情况下使用独立的垃圾回收线程,跟踪可达对象。在每个年老代垃圾回收周期中,在收集初期并发收集器会对整个应用进行简短的暂停,在收集中还会重新标记再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。并发收集器使用处理器换来短暂的停顿时间。在一个N个处理器的系统上,并发收集部分使用K/N个可用处理器进行回收,一般情况下1<=K<=N/4。
使用-XX:+UseConcMarkSweepGC
来开启:
Concurrent Mark Sweep 并发标记清除,与用户线程一起执行
标记-清除算法
并发阶段会降低吞吐量
老年代收集器(新生代使用ParNew)
4.1 CMS标记的4个过程
初始标记(CMS initial mark):
只标记GC Roots能直接关联到的对象。速度快并发标记(CMS concurrent mark):
进行GC RootsTracing的过程。主要标记过程,标记全部对象重新标记(CMS remark):
修正并发标记期间因用户程序继续运行而导致标记发生改变的那一部分对象的标记。由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正并发清除(CMS concurrent sweep):
基于标记结果,直接清理对象
注意:其中初始标记和重新标记两个阶段仍然需要Stop-The-World,整个过程中耗时最长的并发标记和并发清 除过程中收集器都可以和用户线程一起工作。
4.2 CMS收集器的特点
尽可能降低停顿
会影响系统整体吞吐量和性能
比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半。并发收集器在应用运行时进行收集,所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用,否则,垃圾回收还未完成,堆空间先满了。这种情况下将会发生“并发模式失败”,此时整个应用将会暂停,进行垃圾回收。
清理不彻底
因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理,由于在应用运行的同时进行垃圾回收,所以有些垃圾可能在垃圾回收进行完成时产生,这样就造成了“Floating Garbage”,这些垃圾需要在下次垃圾回收周期时才能回收掉。所以,并发收集器一般需要20%的预留空间用于这些浮动垃圾。
因为和用户线程一起运行,不能在空间快满时再清理
适用场景:对响应时间有高要求,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
5.垃圾回收器参数设置
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器(ParNew + CMS + Serial Old),Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。
-XX:ParallelCMSThreads:设定CMS的线程数量,CMS 默认启动的线程数是(ParallelGCThreads+3)/4)
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发,默认值为68%,仅在CMS收集器时有效,-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理,整理过程是独占的,会引起停顿时间变长
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩,通常与UseCMSCompactAtFullCollection参数一起使用
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
-XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收,默认92%
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收