JVM垃圾收集器详解

概述

评估一个垃圾收集(GC)算法如何,根据如下两个标准:

1. 吞吐量(throughput)

吞吐量是指应用程序线程用时占程序总用时的比例。 例如,吞吐量99/100意味着100秒的程序执行时间应用程序线程运行了99秒,而在这一时间段内GC线程只运行了1秒。

2. 暂停时间(pause times)

暂停时间是指一个时间段内应用程序线程让与GC线程执行而完全暂停。 例如,GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有应用程序线程是活动的。

单线程收集器

1. 新生代Serial收集器

特点:
  1. 它仅仅使用单线程进行垃圾回收。
  2. 它是独占式的垃圾回收。
  3. 进行垃圾回收时,Java应用程序中的线程都需要暂停(Stop-The-World)。
  4. 使用复制算法。
  5. 适合CPU等硬件不是很好的场合。
设置参数:
-XX:+UseSerialGC 指定新生使用新生代串行收集器和老年代串行收集器,当以client模式运行时,它是默认的垃圾收集器。

2. 老年代Serial Old收集器

特点:
  1. 同Serial收集器一样,单线程,独占式的垃圾收集器。
  2. 通常老年代垃圾回收比新生代回收要更长时间,所以可能会使应用程序停顿较长时间。
  3. 使用“标记-整理”算法。
  4. 作为CMS收集器的后备预案,当并发收集器发生Concurrent Mode Failure时使用。
设置参数:
-XX:+UseSerialGC 新生代,老年代都使用串行收集器。
-XX:+UseParNewGC 新生代使用ParNew收集器,老年代使用串行收集器。
-XX:+UseParallelGC 新生代使用ParallelGC收集器,老年代使用串行收集器。

多线程收集器

1. 新生代ParNew收集器

特点:
  1. Serial收集器的多线程版本。
  2. 使用复制算法。
  3. 垃圾回收时,应用程序仍会暂停,只不过由于是多线程回收,在多核CPU上,回收效率会高于串行收集器,反之在单核CPU,效率会不如串行收集器。
设置参数:
-XX:+UseParNewGC 新生代使用ParNew收集器,老年代使用串行收集器 。
-XX:+UseConcMarkSweepGC 新生代使用ParNew收集器,老年代使用CMS收集器 。
-XX:ParallelGCThreads=n 指回ParNew收集器工作时的线程数量,cpu核数小时8时,其值等于cpu数量,高于8时,可以使用公式(3+((5*CPU_count)/8))。

2. 新生代ParallelGC收集器

特点:
  1. 同ParNew收集器一样,不同的地方在于,它非常关注系统的吞吐量(通过参数控制)。
  2. 使用复制算法,并行的多线程收集器。
  3. “吞吐量优先”的收集器,适用于在后台运算而不需要太多交互的任务。
  4. 支持自适应的GC调节策略。
设置参数:
-XX:+UseParallelGC  新生代用ParallelGC收集器,老年代使用串行收集器 。
-XX:+UseParallelOldGC  新生代用ParallelGC收集器,老年代使用ParallelOldGC收集器系统吞吐量的控制。
-XX:MaxGCPauseMillis=n(单位ms)   设置垃圾回收的最大停顿时间。
-XX:GCTimeRatio=n(n在0-100之间)  设置吞吐量的大小,假设值为n,那系统将花费不超过1/(n+1)的时间用于垃圾回收 。
-XX:+UseAdaptiveSizePolicy  打开自适应GC策略,在这种模式下,新生代的大小,eden和survivior的比例,晋升老年代的对象年龄等参数会被自动调整,以达到堆大小,吞吐量,停顿时间之间的平衡点。

3. 老年代ParallelOldGC收集器

特点:
  1. 同新生代的ParallelGC收集器一样,是属于老年代的关注吞吐量的多线程并发收集器。
  2. 使用多线程和“标记-整理”算法。
设置参数:
-XX:+UseParallelOldGC  新生代用ParallelGC收集器,老年代使用ParallelOldGC收集器,是非常关注系统吞吐量的收集器组合,适合用于对吞吐量要求较高的系统。
-XX:ParallelGCThreads=n   指回ParNew收集器工作时的线程数量,cpu核数小时8时,其值等于cpu数量,高于8时可以使用公式(3+((5*CPU_count)/8))。

CMS收集器(Concurrent Mark Sweep,并发标记清除)

1. 特点:

  1. 是并发回收,非独占式的收集器,大部分时候应用程序不会停止运行,以获取最短回收停顿时间为目标。
  2. 针对年老代的收集器。
  3. 使用并发“标记-清除”算法,因此回收后会有内存碎片,可以使参数设置进行内存碎片的压缩整理。
  4. 与ParallelGC和ParallelOldGC不同,CMS主要关注系统停顿时间。

2. 主要步骤:

  1. 初始标记:为了收集应用程序的对象引用需要暂停应用程序线程,该阶段完成后,应用程序线程再次启动。
  2. 并发标记:从第一阶段收集到的对象引用开始,遍历所有其他的对象引用。
  3. 重新标记:由于第三阶段是并发的,对象引用可能会发生进一步改变。因此,应用程序线程会再一次被暂停以更新这些变化,并且在进行实际的清理之前确保一个正确的对象引用视图。这一阶段十分重要,因为必须避免收集到仍被引用的对象。
  4. 并发清理:所有不再被应用的对象将从堆里清除掉。
notes:初始标记与重新标记是独占系统资源的,不能与用户线程一起执行,而其它阶段则可以与用户线程一起执行。

3. 参数设置:

-XX:+UseConcMarkSweepGC 该标志首先是激活CMS收集器。默认HotSpot JVM使用的是并行收集器。老年代使用CMS收集器,新生代使用ParNew收集器。
-XX:+CMSConcurrentMTEnabled  当该标志被启用时,并发的CMS阶段将以多线程执行,默认开启。
-XX:ConcGCThreads=n  定义并发CMS过程运行时的线程数(早期JVM版本也叫-XX:ParallelCMSThreads)。
-XX:CMSInitiatingOccupancyFraction=n  指定老年代回收阀值,即当老年代内存使用率达到这个值时,会执行一次CMS回收,JDK1.6默认值为92,设置技巧: (Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100)>=Xmn。
-XX:+UseCMSInitiatingOccupancyOnly  表示只有达到阀值时才进行CMS回收。
-XX:+CMSClassUnloadingEnabled  开启回收Perm区的内存,默认情况下,是需要触发一次FullGC 。
-XX:+CMSIncrementalMode  在增量模式下,CMS收集器在并发阶段,不会独占整个周期,而会周期性的暂停,唤醒应用线程。收集器把并发阶段工作,划分为片段,安排在次级(minor)回收之间运行。这对需要低延迟,运行在少量CPU服务器上的应用很有用。
-XX:+DisableExplicitGC 禁止程序中调用System.gc(),System.gc()的调用,会使用FullGC的方式回收整个堆而会忽略CMS或G1等相关收集器。
-XX:-CMSPrecleaningEnabled 关闭预清理,不进行预清理,默认在并发标记后,会有一个预清理的操作,可减少停顿时间。
-XX:+UseCMSCompactAtFullCollection  开启内存碎片的整理,即当CMS垃圾回收完成后,进行一次内存碎片整理,要注意内存碎片的整理并不是并发进行的,因此可能会引起程序停顿 
-XX:CMSFullGCsBeforeCompation=n  用于指定进行多少次CMS回收后, 再进行一次内存压缩 
-XX:+CMSParallelRemarkEnabled  在使用UseParNewGC 的情况下,尽量减少 mark 的时间。
-XX:+ExplicitGCInvokesConcurrent JVM无论什么时候调用系统GC,都执行CMS GC,而不是Full GC。
-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses 当有系统GC调用时,永久代也被包括进CMS垃圾回收的范围内。

4. CMS缺点:

1)堆碎片
CMS收集器并没有任何碎片整理的机制,因此有可能出现总的堆大小远没有耗尽,但却因为没有足够连续的空间完全容纳对象而不能分配对象。当这种事发生后,并发算法不会帮上任何忙,因此会触发Full GC。Full GC将运行吞吐量收集器的算法,从而解决碎片问题,但却暂停了应用程序线程。因此尽管CMS收集器带来完全的并发性,但仍然有可能发生长时间的“stop-the-world”的风险。
2)无法收集浮动垃圾(Floating Garbage)
在CMS并发清理阶段用户线程还在运行中,伴随着程序的运行,自然会有新的垃圾产生,而这部分垃圾产生于标记过程之后,CMS无法在当此收集中清理掉它们,只好等待下一次GC清理。这部分垃圾就称为“浮动垃圾”。浮动垃圾可能会导致"Concurrent Mode Failure"失败而引发另一次Full GC的产生。
3)对象分配率高
如果获取对象实例的频率高于收集器清除堆里死对象的频率,并发算法将再次失败。从某种程度上说,老年代将没有足够的可用空间来容纳一个从年轻代提升过来的对象。这种情况被称为“并发模式失败”,并且JVM会执行堆碎片整理:触发Full GC。此种情形经常被证实是老年代有大量不必要的对象。一个可行的办法就是增加年轻代的堆大小,以防止年轻代短生命的对象提前进入老年代。另一个办法就是快照运行系统的堆转储,并且分析过度的对象分配,找出这些对象,最终减少这些对象的申请。

G1收集器

1. 概述:

G1收集器将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留的新生代和老年代的概念,但是新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。
G1跟踪各个Region里面垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是First Garbage名称的由来)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的回收效率。
在G1收集器中,Region之间的对象引用以及其他收集器中的新生代和老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全局扫描的。

2. 特点:

  1. 独特的垃圾回收策略,属于分代垃圾收集器。
  2. 使用分区算法,不要求eden,年轻代或老年代的空间都连续。
  3. 并行性: 回收期间,可由多个线程同时工作,有效利用多核cpu资源。
  4. 并发性: 与应用程序可交替执行,部分工作可以和应用程序同时执行。
  5. 分代收集: 分代收集器,同时兼顾年轻代和老年代。
  6. 空间整合: G1从整体上看是基于“标记-整理”算法实现的收集器,从局部(两个Region之间)上看是基于“复制”算法实现的;这意味着G1运行期间不会产生内存空间碎片。
  7. 可预见性: G1可选取部分区域进行回收,可以缩小回收范围,减少全局停顿。

3. 主要步骤:

  1. 初始标记(Initial Marking)
  2. 并发标记(Concurrent Marking)
  3. 最终标记(Final Marking)
  4. 筛选回收(Live Data Counting and Evacuation)

4. 设置参数:

-XX:+UseG1GC  打开G1收集器开关。
-XX:MaxGCPauseMillis=n  指定目标的最大停顿时间,任何一次停顿时间超过这个值,G1就会尝试调整新生代和老年代的比例,调整堆大小,调整晋升年龄。
-XX:ParallelGCThreads=n  用于设置并行回收时,GC的工作线程数量。
-XX:InitiatingHeapOccpancyPercent=n  指定整个堆的使用率达到多少时,执行一次并发标记周期,默认45, 过大会导致并发标记周期迟迟不能启动,增加FullGC的可能,过小会导致GC频繁,会导致应用程序性能有所下降。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值