文档章节

JVM 垃圾回收器

秋风醉了
 秋风醉了
发布于 04/13 17:16
字数 6492
阅读 24
收藏 0

JVM 垃圾回收器

垃圾回收过程

 

垃圾回收算法

  • 标记-清除算法 (Mark-Sweep)

标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段首先通过根节点,标记所有从根节点开始的较大对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。该算法最大的问题是存在大量的空间碎片,因为回收后的空间是不连续的。在对象的堆空间分配过程中,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续的空间。

  • 复制算法 (Copying)

将现有的内存空间分为两快,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大。因此在真正需要垃圾回收的时刻,复制算法的效率是很高的。又由于对象在垃圾回收过程中统一被复制到新的内存空间中,因此,可确保回收后的内存空间是没有碎片的。该算法的缺点是将系统内存折半。

Java 的新生代串行垃圾回收器中使用了复制算法的思想。新生代分为 eden 空间、from 空间、to 空间 3 个部分。其中 from 空间和 to 空间可以视为用于复制的两块大小相同、地位相等,且可进行角色互换的空间块。from 和 to 空间也称为 survivor 空间,即幸存者空间,用于存放未被回收的对象。

在垃圾回收时,eden 空间中的存活对象会被复制到未使用的 survivor 空间中 (假设是 to),正在使用的 survivor 空间 (假设是 from) 中的年轻对象也会被复制到 to 空间中 (大对象,或者老年对象会直接进入老年带,如果 to 空间已满,则对象也会直接进入老年代)。此时,eden 空间和 from 空间中的剩余对象就是垃圾对象,可以直接清空,to 空间则存放此次回收后的存活对象。这种改进的复制算法既保证了空间的连续性,又避免了大量的内存空间浪费。

  • 标记-压缩算法(标记-整理) (Mark-Compact)

复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在年轻代经常发生,但是在老年代更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活的对象较多,复制的成本也将很高。

标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。也首先需要从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。

  • 分代 (Generational Collecting)

根据垃圾回收对象的特性,不同阶段最优的方式是使用合适的算法用于本阶段的垃圾回收,分代算法即是基于这种思想,它将内存区间根据对象的特点分成几块,根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。以 Hot Spot 虚拟机为例,它将所有的新建对象都放入称为年轻代的内存区域,年轻代的特点是对象会很快回收,因此,在年轻代就选择效率较高的复制算法。当一个对象经过几次回收后依然存活,对象就会被放入称为老生代的内存空间。在老生代中,几乎所有的对象都是经过几次垃圾回收后依然得以幸存的。因此,可以认为这些对象在一段时期内,甚至在应用程序的整个生命周期中,将是常驻内存的。如果依然使用复制算法回收老生代,将需要复制大量对象。再加上老生代的回收性价比也要低于新生代,因此这种做法也是不可取的。根据分代的思想,可以对老年代的回收使用与新生代不同的标记-压缩算法,以提高垃圾回收效率。

 

JVM 垃圾回收器分类

1.1 Serial 新生代串行收集器

串行收集器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收;第二,它独占式的垃圾回收。

在串行收集器进行垃圾回收时,Java 应用程序中的线程都需要暂停,等待垃圾回收的完成,这样给用户体验造成较差效果。虽然如此,串行收集器却是一个成熟、经过长时间生产环境考验的极为高效的收集器。新生代串行处理器使用复制算法,实现相对简单,逻辑处理特别高效,且没有线程切换的开销。在诸如单 CPU 处理器或者较小的应用内存等硬件平台不是特别优越的场合,它的性能表现可以超过并行回收器和并发回收器。在 HotSpot 虚拟机中,使用-XX:+UseSerialGC 参数可以指定使用新生代串行收集器(serial)和老年代串行收集器(serial old)。当 JVM 在 Client 模式下运行时,它是默认的垃圾收集器。一次新生代串行收集器的工作输出日志类似如清单 1 信息 (使用-XX:+PrintGCDetails 开关) 所示。

一次新生代串行收集器的工作输出日志

0.154: [GC (Allocation Failure) 0.154: [DefNew: 6759K->471K(18432K), 0.0046125 secs] 6759K->5591K(49152K), 0.0047263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.166: [Full GC (System.gc()) 0.166: [Tenured: 5120K->15828K(30720K), 0.0130598 secs] 16159K->15828K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0131750 secs] [Times: user=0.00 sys=0.01, real=0.02 secs] 
0.180: [GC (Allocation Failure) 0.180: [DefNew: 0K->0K(18432K), 0.0014763 secs]0.181: [Tenured: 15828K->15828K(30720K), 0.0025240 secs] 15828K->15828K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0041812 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.184: [Full GC (Allocation Failure) 0.184: [Tenured: 15828K->15818K(30720K), 0.0020868 secs] 15828K->15818K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0021533 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 18432K, used 814K [0x00000007bce00000, 0x00000007be200000, 0x00000007be200000)
  eden space 16384K,   4% used [0x00000007bce00000, 0x00000007bcecbb30, 0x00000007bde00000)
  from space 2048K,   0% used [0x00000007bde00000, 0x00000007bde00000, 0x00000007be000000)
  to   space 2048K,   0% used [0x00000007be000000, 0x00000007be000000, 0x00000007be200000)
 tenured generation   total 30720K, used 15818K [0x00000007be200000, 0x00000007c0000000, 0x00000007c0000000)
   the space 30720K,  51% used [0x00000007be200000, 0x00000007bf172810, 0x00000007bf172a00, 0x00000007c0000000)
 Metaspace       used 2834K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

[GC (Allocation Failure) 0.154: [DefNew: 6759K->471K(18432K), 0.0046125 secs] 6759K->5591K(49152K), 0.0047263 secs]

[DefNew: 6759K->471K(18432K), 0.0046125 secs] :表示DefNew内存区域GC前该内存区域已使用的容量->GC后该内存区域已使用的容量(该内存区域总容量)

6759K->5591K(49152K) :GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)。

1.2 Serial Old 老年代串行收集器

老年代串行收集器使用的是标记-压缩算法。和新生代串行收集器一样,它也是一个串行的、独占式的垃圾回收器。由于老年代垃圾回收通常会使用比新生代垃圾回收更长的时间,因此,在堆空间较大的应用程序中,一旦老年代串行收集器启动,应用程序很可能会因此停顿几秒甚至更长时间。虽然如此,老年代串行回收器可以和多种新生代回收器配合使用,同时它也可以作为 CMS 回收器的备用回收器。

若要启用老年代串行回收器,可以尝试使用以下参数:-XX:+UseSerialGC: 新生代、老年代都使用串行回收器。如下所示,

0.154: [GC (Allocation Failure) 0.154: [DefNew: 6759K->471K(18432K), 0.0046125 secs] 6759K->5591K(49152K), 0.0047263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.166: [Full GC (System.gc()) 0.166: [Tenured: 5120K->15828K(30720K), 0.0130598 secs] 16159K->15828K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0131750 secs] [Times: user=0.00 sys=0.01, real=0.02 secs] 
0.180: [GC (Allocation Failure) 0.180: [DefNew: 0K->0K(18432K), 0.0014763 secs]0.181: [Tenured: 15828K->15828K(30720K), 0.0025240 secs] 15828K->15828K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0041812 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.184: [Full GC (Allocation Failure) 0.184: [Tenured: 15828K->15818K(30720K), 0.0020868 secs] 15828K->15818K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0021533 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 18432K, used 814K [0x00000007bce00000, 0x00000007be200000, 0x00000007be200000)
  eden space 16384K,   4% used [0x00000007bce00000, 0x00000007bcecbb30, 0x00000007bde00000)
  from space 2048K,   0% used [0x00000007bde00000, 0x00000007bde00000, 0x00000007be000000)
  to   space 2048K,   0% used [0x00000007be000000, 0x00000007be000000, 0x00000007be200000)
 tenured generation   total 30720K, used 15818K [0x00000007be200000, 0x00000007c0000000, 0x00000007c0000000)
   the space 30720K,  51% used [0x00000007be200000, 0x00000007bf172810, 0x00000007bf172a00, 0x00000007c0000000)
 Metaspace       used 2834K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

如果使用-XX:+UseParNewGC 参数设置,表示新生代使用并行收集器,老年代使用串行收集器,如下所示,

0.156: [GC (Allocation Failure) 0.156: [ParNew: 6759K->497K(18432K), 0.0068128 secs] 6759K->5617K(49152K), 0.0070460 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
0.170: [Full GC (System.gc()) 0.170: [Tenured: 5120K->15828K(30720K), 0.0218059 secs] 16185K->15828K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0223104 secs] [Times: user=0.01 sys=0.01, real=0.03 secs] 
0.193: [GC (Allocation Failure) 0.193: [ParNew: 0K->0K(18432K), 0.0009438 secs]0.194: [Tenured: 15828K->15828K(30720K), 0.0016665 secs] 15828K->15828K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0027300 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.195: [Full GC (Allocation Failure) 0.195: [Tenured: 15828K->15818K(30720K), 0.0017794 secs] 15828K->15818K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0018435 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 18432K, used 814K [0x00000007bce00000, 0x00000007be200000, 0x00000007be200000)
  eden space 16384K,   4% used [0x00000007bce00000, 0x00000007bcecbb30, 0x00000007bde00000)
  from space 2048K,   0% used [0x00000007bde00000, 0x00000007bde00000, 0x00000007be000000)
  to   space 2048K,   0% used [0x00000007be000000, 0x00000007be000000, 0x00000007be200000)
 tenured generation   total 30720K, used 15818K [0x00000007be200000, 0x00000007c0000000, 0x00000007c0000000)
   the space 30720K,  51% used [0x00000007be200000, 0x00000007bf172810, 0x00000007bf172a00, 0x00000007c0000000)
 Metaspace       used 2833K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

1.3 ParNew 并行收集器

并行收集器是工作在新生代的垃圾收集器,它只简单地将串行回收器多线程化。它的回收策略、算法以及参数和串行回收器一样。

并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的 CPU 上,它产生的停顿时间要短于串行回收器,而在单 CPU 或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程的压力,它的实际表现很可能比串行回收器差。

开启并行回收器可以使用参数-XX:+UseParNewGC,该参数设置新生代使用并行收集器,老年代使用串行收集器。如下所示,

0.156: [GC (Allocation Failure) 0.156: [ParNew: 6759K->497K(18432K), 0.0068128 secs] 6759K->5617K(49152K), 0.0070460 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
0.170: [Full GC (System.gc()) 0.170: [Tenured: 5120K->15828K(30720K), 0.0218059 secs] 16185K->15828K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0223104 secs] [Times: user=0.01 sys=0.01, real=0.03 secs] 
0.193: [GC (Allocation Failure) 0.193: [ParNew: 0K->0K(18432K), 0.0009438 secs]0.194: [Tenured: 15828K->15828K(30720K), 0.0016665 secs] 15828K->15828K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0027300 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.195: [Full GC (Allocation Failure) 0.195: [Tenured: 15828K->15818K(30720K), 0.0017794 secs] 15828K->15818K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0018435 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 18432K, used 814K [0x00000007bce00000, 0x00000007be200000, 0x00000007be200000)
  eden space 16384K,   4% used [0x00000007bce00000, 0x00000007bcecbb30, 0x00000007bde00000)
  from space 2048K,   0% used [0x00000007bde00000, 0x00000007bde00000, 0x00000007be000000)
  to   space 2048K,   0% used [0x00000007be000000, 0x00000007be000000, 0x00000007be200000)
 tenured generation   total 30720K, used 15818K [0x00000007be200000, 0x00000007c0000000, 0x00000007c0000000)
   the space 30720K,  51% used [0x00000007be200000, 0x00000007bf172810, 0x00000007bf172a00, 0x00000007c0000000)
 Metaspace       used 2833K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

并行收集器工作时的线程数量可以使用-XX:ParallelGCThreads 参数指定。一般,最好与 CPU 数量相当,避免过多的线程数影响垃圾收集性能。在默认情况下,当 CPU 数量小于 8 个,ParallelGCThreads 的值等于 CPU 数量,大于 8 个,ParallelGCThreads 的值等于 3+[5*CPU_Count]/8]。

设置参数-XX:+UseConcMarkSweepGC 可以要求新生代使用并行收集器,老年代使用 CMS。

1.4 新生代并行回收 (Parallel Scavenge) 收集器

新生代并行回收收集器也是使用复制算法的收集器。从表面上看,它和并行收集器一样都是多线程、独占式的收集器。但是,并行回收收集器有一个重要的特点:它非常关注系统的吞吐量。

新生代并行回收收集器可以使用以下参数启用:

-XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。

-XX:+UseParallelOldGC:新生代和老年代都是用并行回收收集器。

如下所示,

0.157: [GC (System.gc()) [PSYoungGen: 6759K->576K(18432K)] 16999K->15944K(49152K), 0.0092261 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
0.166: [Full GC (System.gc()) [PSYoungGen: 576K->0K(18432K)] [ParOldGen: 15368K->15830K(30720K)] 15944K->15830K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0081688 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
0.175: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] 15830K->15830K(49152K), 0.0004951 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.175: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] 15830K->15830K(49152K), 0.0004297 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.176: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] [ParOldGen: 15830K->15830K(30720K)] 15830K->15830K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0051273 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.181: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] 15830K->15830K(49152K), 0.0005226 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.182: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] [ParOldGen: 15830K->15819K(30720K)] 15830K->15819K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0040339 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] 
Heap
 PSYoungGen      total 18432K, used 491K [0x00000007bec00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 16384K, 3% used [0x00000007bec00000,0x00000007bec7afa0,0x00000007bfc00000)
  from space 2048K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007c0000000)
  to   space 2048K, 0% used [0x00000007bfc00000,0x00000007bfc00000,0x00000007bfe00000)
 ParOldGen       total 30720K, used 15819K [0x00000007bce00000, 0x00000007bec00000, 0x00000007bec00000)
  object space 30720K, 51% used [0x00000007bce00000,0x00000007bdd72f30,0x00000007bec00000)
 Metaspace       used 2833K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

新生代并行回收收集器可以使用以下参数启用:

-XX:+MaxGCPauseMills:设置最大垃圾收集停顿时间,它的值是一个大于 0 的整数。收集器在工作时会调整 Java 堆大小或者其他一些参数,尽可能地把停顿时间控制在 MaxGCPauseMills 以内。如果希望减少停顿时间,而把这个值设置得很小,为了达到预期的停顿时间,JVM 可能会使用一个较小的堆 (一个小堆比一个大堆回收快),而这将导致垃圾回收变得很频繁,从而增加了垃圾回收总时间,降低了吞吐量。

-XX:+GCTimeRatio:设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。比如 GCTimeRatio 等于 19,则系统用于垃圾收集的时间不超过 1/(1+19)=5%。默认情况下,它的取值是 99,即不超过 1%的时间用于垃圾收集。

除此之外,并行回收收集器与并行收集器另一个不同之处在于,它支持一种自适应的 GC 调节策略,使用-XX:+UseAdaptiveSizePolicy 可以打开自适应 GC 策略。在这种模式下,新生代的大小、eden 和 survivor 的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。在手工调优比较困难的场合,可以直接使用这种自适应的方式,仅指定虚拟机的最大堆、目标的吞吐量 (GCTimeRatio) 和停顿时间 (MaxGCPauseMills),让虚拟机自己完成调优工作。

1.5 Parallel Old 老年代并行回收收集器

老年代的并行回收收集器也是一种多线程并发的收集器。和新生代并行回收收集器一样,它也是一种关注吞吐量的收集器。老年代并行回收收集器使用标记-压缩算法,JDK1.6 之后开始启用。

使用-XX:+UseParallelOldGC 可以在新生代和老生代都使用并行回收收集器,这是一对非常关注吞吐量的垃圾收集器组合,在对吞吐量敏感的系统中,可以考虑使用。参数-XX:ParallelGCThreads 也可以用于设置垃圾回收时的线程数量。

如下所示,

0.157: [GC (System.gc()) [PSYoungGen: 6759K->576K(18432K)] 16999K->15944K(49152K), 0.0098925 secs] [Times: user=0.02 sys=0.01, real=0.01 secs] 
0.167: [Full GC (System.gc()) [PSYoungGen: 576K->0K(18432K)] [ParOldGen: 15368K->15830K(30720K)] 15944K->15830K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0091739 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
0.176: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] 15830K->15830K(49152K), 0.0004746 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.177: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] 15830K->15830K(49152K), 0.0005598 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.177: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] [ParOldGen: 15830K->15830K(30720K)] 15830K->15830K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0073636 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
0.185: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] 15830K->15830K(49152K), 0.0005012 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.186: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(18432K)] [ParOldGen: 15830K->15819K(30720K)] 15830K->15819K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0041625 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 18432K, used 491K [0x00000007bec00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 16384K, 3% used [0x00000007bec00000,0x00000007bec7afa0,0x00000007bfc00000)
  from space 2048K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007c0000000)
  to   space 2048K, 0% used [0x00000007bfc00000,0x00000007bfc00000,0x00000007bfe00000)
 ParOldGen       total 30720K, used 15819K [0x00000007bce00000, 0x00000007bec00000, 0x00000007bec00000)
  object space 30720K, 51% used [0x00000007bce00000,0x00000007bdd72f30,0x00000007bec00000)
 Metaspace       used 2833K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

1.6 CMS 收集器

与并行回收收集器不同,CMS 收集器主要关注于系统停顿时间。CMS 是 Concurrent Mark Sweep 的缩写,意为并发标记清除,从名称上可以得知,它使用的是标记-清除算法,同时它又是一个使用多线程并发回收的垃圾收集器。

CMS 工作时,主要步骤有:初始标记、并发标记、重新标记、并发清除和并发重置。其中初始标记和重新标记是独占系统资源的,而并发标记、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上来说,CMS 收集不是独占式的,它可以在应用程序运行过程中进行垃圾回收。

根据标记-清除算法,初始标记、并发标记和重新标记都是为了标记出需要回收的对象。并发清理则是在标记完成后,正式回收垃圾对象;并发重置是指在垃圾回收完成后,重新初始化 CMS 数据结构和数据,为下一次垃圾回收做好准备。并发标记、并发清理和并发重置都是可以和应用程序线程一起执行的。

CMS 收集器在其主要的工作阶段虽然没有暴力地彻底暂停应用程序线程,但是由于它和应用程序线程并发执行,相互抢占 CPU,所以在 CMS 执行期内对应用程序吞吐量造成一定影响。CMS 默认启动的线程数是 (ParallelGCThreads+3)/4),ParallelGCThreads 是新生代并行收集器的线程数,也可以通过-XX:ParallelCMSThreads 参数手工设定 CMS 的线程数量。当 CPU 资源比较紧张时,受到 CMS 收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕。

由于 CMS 收集器不是独占式的回收器,在 CMS 回收过程中,应用程序仍然在不停地工作。在应用程序工作过程中,又会不断地产生垃圾。这些新生成的垃圾在当前 CMS 回收过程中是无法清除的。同时,因为应用程序没有中断,所以在 CMS 回收过程中,还应该确保应用程序有足够的内存可用。因此,CMS 收集器不会等待堆内存饱和时才进行垃圾回收,而是当前堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在 CMS 工作过程中依然有足够的空间支持应用程序运行。

这个回收阈值可以使用-XX:CMSInitiatingOccupancyFraction 来指定,默认是 68。即当老年代的空间使用率达到 68%时,会执行一次 CMS 回收。如果应用程序的内存使用率增长很快,在 CMS 的执行过程中,已经出现了内存不足的情况,此时,CMS 回收将会失败,JVM 将启动老年代串行收集器进行垃圾回收。如果这样,应用程序将完全中断,直到垃圾收集完成,这时,应用程序的停顿时间可能很长。因此,根据应用程序的特点,可以对-XX:CMSInitiatingOccupancyFraction 进行调优。如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低 CMS 的触发频率,减少老年代回收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。

标记-清除算法将会造成大量内存碎片,离散的可用空间无法分配较大的对象。在这种情况下,即使堆内存仍然有较大的剩余空间,也可能会被迫进行一次垃圾回收,以换取一块可用的连续内存,这种现象对系统性能是相当不利的,为了解决这个问题,CMS 收集器还提供了几个用于内存压缩整理的算法。

-XX:+UseCMSCompactAtFullCollection 参数可以使 CMS 在垃圾收集完成后,进行一次内存碎片整理。内存碎片的整理并不是并发进行的。-XX:CMSFullGCsBeforeCompaction 参数可以用于设定进行多少次 CMS 回收后,进行一次内存压缩。

设置参数-XX:+UseConcMarkSweepGC,-XX:CMSInitiatingOccupancyFraction 设置为 100,同时设置-XX:+UseCMSCompactAtFullCollection 和-XX:CMSFullGCsBeforeCompaction,日志输出如下:

0.168: [GC (Allocation Failure) 0.168: [ParNew: 6759K->490K(18432K), 0.0078723 secs] 6759K->5612K(49152K), 0.0080426 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
0.184: [Full GC (System.gc()) 0.184: [CMS: 5122K->15833K(30720K), 0.0189607 secs] 16180K->15833K(49152K), [Metaspace: 2802K->2802K(1056768K)], 0.0190976 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
0.203: [GC (CMS Initial Mark) [1 CMS-initial-mark: 15833K(30720K)] 15833K(49152K), 0.0002042 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.203: [CMS-concurrent-mark-start]
0.204: [GC (Allocation Failure) 0.204: [ParNew: 327K->101K(18432K), 0.0014503 secs]0.205: [CMS0.207: [CMS-concurrent-mark: 0.002/0.004 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] 
 (concurrent mode failure): 15833K->15833K(30720K), 0.0082671 secs] 16160K->15833K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0100232 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] 
0.214: [Full GC (Allocation Failure) 0.214: [CMS: 15833K->15822K(30720K), 0.0065012 secs] 15833K->15822K(49152K), [Metaspace: 2803K->2803K(1056768K)], 0.0066840 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
Heap
 par new generation   total 18432K, used 650K [0x00000007bce00000, 0x00000007be200000, 0x00000007be200000)
  eden space 16384K,   3% used [0x00000007bce00000, 0x00000007bcea2bc8, 0x00000007bde00000)
  from space 2048K,   0% used [0x00000007bde00000, 0x00000007bde00000, 0x00000007be000000)
  to   space 2048K,   0% used [0x00000007be000000, 0x00000007be000000, 0x00000007be200000)
 concurrent mark-sweep generation total 30720K, used 15822K [0x00000007be200000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 2833K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

1.7 G1 收集器 (Garbage First)

G1 收集器的目标是作为一款服务器的垃圾收集器,因此,它在吞吐量和停顿控制上,预期要优于 CMS 收集器。

与 CMS 收集器相比,G1 收集器是基于标记-压缩算法的。因此,它不会产生空间碎片,也没有必要在收集完成后,进行一次独占式的碎片整理工作。G1 收集器还可以进行非常精确的停顿控制。它可以让开发人员指定当停顿时长为 M 时,垃圾回收时间不超过 N。使用参数-XX:+UnlockExperimentalVMOptions –XX:+UseG1GC 来启用 G1 回收器,设置 G1 回收器的目标停顿时间:-XX:MaxGCPauseMills=20,-XX:GCPauseIntervalMills=200。如下所示,

0.162: [Full GC (System.gc())  16M->15M(50M), 0.0032349 secs]
   [Eden: 2048.0K(20.0M)->0.0B(20.0M) Survivors: 0.0B->0.0B Heap: 16.4M(50.0M)->15.5M(50.0M)], [Metaspace: 2802K->2802K(1056768K)]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.166: [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0004755 secs]
   [Parallel Time: 0.2 ms, GC Workers: 4]
      [GC Worker Start (ms): Min: 165.7, Avg: 165.7, Max: 165.7, Diff: 0.0]
      [Ext Root Scanning (ms): Min: 0.1, Avg: 0.2, Max: 0.2, Diff: 0.0, Sum: 0.6]
      [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
         [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Object Copy (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
      [GC Worker Total (ms): Min: 0.2, Avg: 0.2, Max: 0.2, Diff: 0.0, Sum: 0.7]
      [GC Worker End (ms): Min: 165.9, Avg: 165.9, Max: 165.9, Diff: 0.0]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.0 ms]
   [Other: 0.2 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.1 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.1 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 0.0B(20.0M)->0.0B(20.0M) Survivors: 0.0B->0.0B Heap: 15.5M(50.0M)->15.5M(50.0M)]
 [Times: user=0.00 sys=0.00, real=0.01 secs] 
0.166: [GC concurrent-root-region-scan-start]
0.166: [GC concurrent-root-region-scan-end, 0.0000054 secs]
0.166: [GC concurrent-mark-start]
0.166: [GC pause (G1 Humongous Allocation) (young), 0.0005410 secs]
   [Parallel Time: 0.2 ms, GC Workers: 4]
      [GC Worker Start (ms): Min: 166.4, Avg: 166.4, Max: 166.4, Diff: 0.0]
      [Ext Root Scanning (ms): Min: 0.1, Avg: 0.1, Max: 0.1, Diff: 0.0, Sum: 0.5]
      [SATB Filtering (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
         [Processed Buffers: Min: 0, Avg: 0.2, Max: 1, Diff: 1, Sum: 1]
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Object Copy (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [GC Worker Total (ms): Min: 0.1, Avg: 0.1, Max: 0.2, Diff: 0.0, Sum: 0.6]
      [GC Worker End (ms): Min: 166.6, Avg: 166.6, Max: 166.6, Diff: 0.0]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.1 ms]
   [Other: 0.3 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.2 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.1 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 0.0B(20.0M)->0.0B(20.0M) Survivors: 0.0B->0.0B Heap: 15.5M(50.0M)->15.5M(50.0M)]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.168: [Full GC (Allocation Failure)  15M->15M(50M), 0.0059527 secs]
   [Eden: 0.0B(20.0M)->0.0B(20.0M) Survivors: 0.0B->0.0B Heap: 15.5M(50.0M)->15.5M(50.0M)], [Metaspace: 2802K->2802K(1056768K)]
 [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.174: [Full GC (Allocation Failure)  15M->15M(50M), 0.0023144 secs]
   [Eden: 0.0B(20.0M)->0.0B(20.0M) Survivors: 0.0B->0.0B Heap: 15.5M(50.0M)->15.4M(50.0M)], [Metaspace: 2802K->2802K(1056768K)]
 [Times: user=0.00 sys=0.00, real=0.01 secs] 
0.177: [GC concurrent-mark-abort]
Heap
 garbage-first heap   total 51200K, used 15819K [0x00000007bce00000, 0x00000007bcf00190, 0x00000007c0000000)
  region size 1024K, 1 young (1024K), 0 survivors (0K)
 Metaspace       used 2833K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 306K, capacity 386K, committed 512K, reserved 1048576K

=========END=========

本文转载自:https://www.ibm.com/developerworks/cn/java/j-lo-JVMGarbageCollection/

共有 人打赏支持
秋风醉了
粉丝 239
博文 572
码字总数 416654
作品 0
朝阳
程序员
私信 提问
Java finalize方法

《JAVA编程思想》: java提供finalize()方法,垃圾回收器准备释放内存的时候,会先调用finalize()。 (1).对象不一定会被回收。 (2).垃圾回收不是析构函数。 (3).垃圾回收只与内存有关。 (4)....

清风伴月
2017/10/22
0
0
Java GC系列:Java垃圾回收详解

Java的内存分配与回收全部由JVM垃圾回收进程自动完成。与C语言不同,Java开发者不需要自己编写代码实现垃圾回收。这是Java深受大家欢迎的众多特性之一,能够帮助程序员更好地编写Java程序。 ...

满风
2015/04/10
0
0
《成神之路-基础篇》JVM——垃圾回收(已完结)

Java内存模型,Java内存管理,Java堆和栈,垃圾回收 本文是[《成神之路系列文章》][1]的第一篇,主要是关于JVM的一些介绍。 持续更新中 Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收 ...

05/05
0
0
Java的垃圾回收之算法[转]

引言 Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对象通过new、newarray、anewarray和multianewarray等...

kext
2012/03/20
0
0
JVM系列第9讲:JVM垃圾回收器

前面文章中,我们介绍了 Java 虚拟机的内存结构,Java 虚拟机的垃圾回收机制,那么这篇文章我们说说具体执行垃圾回收的垃圾回收器。 总的来说,Java 虚拟机的垃圾回收器可以分为四大类别:串...

陈树义
11/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

EOS docker开发环境

使用eos docker镜像是部署本地EOS开发环境的最轻松愉快的方法。使用官方提供的eos docker镜像,你可以快速建立一个eos开发环境,可以迅速启动开发节点和钱包服务器、创建账户、编写智能合约....

汇智网教程
今天
10
0
《唐史原来超有趣》的读后感优秀范文3700字

《唐史原来超有趣》的读后感优秀范文3700字: 作者:花若离。我今天分享的内容《唐史原来超有趣》这本书的读后感,我将这本书看了一遍之后就束之高阁了,不过里面的内容一直在在脑海中回放,...

原创小博客
今天
17
0
IC-CAD Methodology知识图谱

CAD (Computer Aided Design),计算机辅助设计,指利用计算机及其图形设备帮助设计人员进行设计工作,这个定义同样可以用来近似描述IC公司CAD工程师这个岗位的工作。 早期IC公司的CAD岗位最初...

李艳青1987
今天
17
0
CompletableFuture get方法一直阻塞或抛出TimeoutException

问题描述 最近刚刚上线的服务突然抛出大量的TimeoutException,查询后发现是使用了CompletableFuture,并且在执行future.get(5, TimeUnit.SECONDS);时抛出了TimeoutException异常,导致接口响...

xiaolyuh
今天
8
0
dubbo 搭建与使用

官网:http://dubbo.apache.org/en-us/ 一,安装监控中心(可以不安装) admin管理控制台,monitor监控中心 下载 bubbo ops 这个是新版的,需要node.js环境,我没有就用老版的了...

小兵胖胖
今天
16
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部