Concurrent Mark Sweep。看名字就知道,CMS是一款并发、使用标记-清除算法的gc。CMS是针对老年代进行回收的GC。 一、哪些对象可以回收1、引用计数法算法过程: 每个对象有一个引用计数器,当对象被引用一次计数器就加一,引用失效就减一,对于计数器为0的对象表示为垃圾对象,可以被GC回收。 请分析以下哪些操作是原子性操作: 缺点: 无法解决循环引用的问题,例如:A引用了B,B引用了A,但是A和B都没有被其他对象引用,这样就会导致内存泄漏,无法被回收。 2、可达性分析法算法过程:通过一系列被称为GC Roots的对象作为起点开始搜索,所经过的路径被称为引用链,当一个对象没有跟任何一个引用链相连接的时候,表示从GC Roots对象到这个对象不可达,意味着这是一个垃圾对象可以被回收。 适用场景:Java虚拟机是采用这种算法对垃圾进行回收,解决了循环引用的问题。 可以作为GC Roots对象有:虚拟机栈,静态成员,常量,本地方法栈引用的对象。 二、垃圾回收算法1、标记清除算法过程:首先标记出需要回收的对象,标记完成后统一回收。 缺点:主要有两个缺点,一个是标记和清除两个过程的效率都不算高(据资料显示)。另一个是空间问题,标记清除后,会产生不连续的内存碎片,当需要分配大对象时,无法找到足够连续的内存,导致分配失败提前触发GC。 2、复制算法算法过程:将内存分为两部分,每次只使用其中一块内存。回收时将存活的对象复制到另一块区域,之后将已使用的内存区域一次性全部清理掉。 优点:解决了空间碎片的问题,存活对象少时,提升了回收效率。 缺点:一个是内存使用率缩小了,因为永远有一块空闲的内存备用。另一个是当存活对象较多时复制效率低下。 3、标记整理算法过程:标记整理的算法过程跟标记清除的标记过程是一样的,但标记后是将存活的对象都向某一端移动,然后清理边界以外的内存。 优点:解决了内存碎片的问题。 可以作为GC Roots对象有:虚拟机栈,静态成员,常量,本地方法栈引用的对象。 三、CMS介绍1、介绍老年代收集器,需要配合Serial或者ParNew使用,一般是与ParNew使用。CMS全称Concurrent Mark-Sweep。CMS出现的目标是为了降低延迟,减少回收停顿时间。适合对延迟、停顿时间敏感的应用使用。通过 -XX:+UseConcMarkSweepGC开启。 2、回收阶段CMS-initial-mark(STW):通过可达性分析算法,从GC Roots对象开始扫描能够直接关联到的对象,并做标记,需要STW,但是一般会很快完成。 CMS-concurrent-mark:从初始标记的基础上,继续向下搜索并做标记,这个阶段应用线程和GC线程并发执行,不会造成停顿。 CMS-concurrent-preclean:查找并发标记阶段中,新进入老年代的对象,包括晋升到老年代和直接在老年代分配的对象,目的是减少下一个阶段重新标记的时间。 CMS-concurrent-abortable-preclean:可中断的预清理,这个阶段做的事情跟并发预清理的事情一样,目的是为了减少下一个阶段STW的时间,这个阶段有几个个条件控制何时结束。 A、-XX:CMSScheduleRemarkEdenSizeThreshold=2.该阶段在Eden区占用超过2M时启动。 B、-XX:CMSMaxAbortablePrecleanTime=5000.设置一个最大时间最大执行5秒钟。 C、-XX:CMSScheduleRemarkEdenPenetration=50.Eden区使用率超过50就停止该阶段进入remark阶段。 D、-XX:+CMSScavengeBeforeRemark,控制remark阶段前进行一次minor gc,来提高remark的效率,减少时间。 CMS-remark(STW):重新标记,处理上几次以来可能引用关系发生变化的部分,并重新进行标记,这个阶段会停止应用线程 CMS-concurrent-sweep:真正的清理阶段,将以上几个步骤标记的无法访问的对象进行并发清理,并将清理的空间回收到空闲列表中 CMS-concurrent-reset:调整堆大小,重置一些内部的数据结构,为下一次回收做准备 3、缺点A、无法处理浮动垃圾,因为这是一款并发的收集器,程序运行和收集的同时都会产生垃圾,所以回收时不能向其他收集器一样等到满了的时候再收集,通过-XX:CMSInitiatingOccupancyFraction=65设置触发的百分比,留出一定的空间给并发收集的时候使用,当CMS运行期间无法满足程序的需要,则会出现,此时会临时使用Serial Old作为临时方案进行一次标记整理的回收,这样就会使得出现较为长期的停顿,所以-XX:CMSInitiatingOccupancyFraction=65不能设置太高。 B、由于是使用标记清除算法的收集器,因此会产生内存碎片,为了解决这个问题,垃圾收集器提供了一个-XX:+UseCMSCompactAtFullCollection的开关参数,表示再一次FGC时进行一次内存整理的过程,这个过程是无法并发的,会拉长停顿时间,收集器还提供另一个参数,-XX:CMSFullGCsBeforeCompaction=5表示FGC几次后进行一次内存整理。 4、优化措施优化目标:减少STW停顿时间 优化手段: A、降低触发CMS回收的百分比,给并发收集时留出一定的空间,避免Concurrent Mode Failure,通过-XX:CMSInitiatingOccupancyFraction=65配置 B、开启内存压缩,避免碎片问题无法分配大内存对象,通过-XX:+UseCMSCompactAtFullCollection和-XX:CMSFullGCsBeforeCompaction=5.具体数值可以按需调配。 C、减少CMS-remark(STW)阶段的时间,通过-XX:+CMSScavengeBeforeRemark、-XX:CMSMaxAbortablePrecleanTime=5000、-XX:CMSScheduleRemarkEdenPenetration=50进行微调。 5、相关参数
6、日志分析以下是一次完整的CMS回收日志,需要配置以下参数 2019-07-17T21:54:50.216+0800: 190007.616: [GC [1 CMS-initial-mark: 1258440K(2097152K)] 1294890K(3984640K), 0.0365050 secs] [Times: user=0.04 sys=0.01, real=0.04 secs]2019-07-17T21:54:50.253+0800: 190007.654: Total time for which application threads were stopped: 0.0455330 seconds2019-07-17T21:54:50.254+0800: 190007.654: [CMS-concurrent-mark-start]2019-07-17T21:54:50.413+0800: 190007.813: [CMS-concurrent-mark: 0.159/0.159 secs] [Times: user=0.91 sys=0.02, real=0.16 secs]2019-07-17T21:54:50.413+0800: 190007.814: [CMS-concurrent-preclean-start]2019-07-17T21:54:50.413+0800: 190007.814: [Preclean SoftReferences, 0.0005560 secs]2019-07-17T21:54:50.414+0800: 190007.814: [Preclean WeakReferences, 0.0011690 secs]2019-07-17T21:54:50.415+0800: 190007.815: [Preclean FinalReferences, 0.0000350 secs]2019-07-17T21:54:50.415+0800: 190007.815: [Preclean PhantomReferences, 0.0000070 secs]2019-07-17T21:54:50.425+0800: 190007.825: [CMS-concurrent-preclean: 0.011/0.011 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]2019-07-17T21:54:50.425+0800: 190007.826: [CMS-concurrent-abortable-preclean-start] CMS: abort preclean due to time 2019-07-17T21:54:51.938+0800: 190009.338: [CMS-concurrent-abortable-preclean: 1.508/1.512 secs] [Times: user=2.46 sys=0.16, real=1.51 secs]2019-07-17T21:54:51.938+0800: 190009.338: Application time: 1.6846120 seconds2019-07-17T21:54:51.946+0800: 190009.346: [GC[YG occupancy: 351451 K (1887488 K)]2019-07-17T21:54:51.946+0800: 190009.347: [GC2019-07-17T21:54:51.949+0800: 190009.350: [ParNew2019-07-17T21:54:51.979+0800: 190009.379: [SoftReference, 2284 refs, 0.0002550 secs]2019-07-17T21:54:51.979+0800: 190009.379: [WeakReference, 5 refs, 0.0000150 secs]2019-07-17T21:54:51.979+0800: 190009.379: [FinalReference, 679 refs, 0.0002280 secs]2019-07-17T21:54:51.979+0800: 190009.380: [PhantomReference, 0 refs, 0.0000120 secs]2019-07-17T21:54:51.979+0800: 190009.380: [JNI Weak Reference, 0.0000100 secs]: 351451K->23085K(1887488K), 0.0330260 secs] 1609892K->1282310K(3984640K), 0.0370030 secs] [Times: user=0.26 sys=0.01, real=0.04 secs]2019-07-17T21:54:51.984+0800: 190009.384: [Rescan (parallel) , 0.0272010 secs]2019-07-17T21:54:52.011+0800: 190009.411: [weak refs processing2019-07-17T21:54:52.011+0800: 190009.411: [SoftReference, 5341 refs, 0.0020150 secs]2019-07-17T21:54:52.013+0800: 190009.413: [WeakReference, 0 refs, 0.0000220 secs]2019-07-17T21:54:52.013+0800: 190009.413: [FinalReference, 80 refs, 0.0002020 secs]2019-07-17T21:54:52.013+0800: 190009.414: [PhantomReference, 0 refs, 0.0000120 secs]2019-07-17T21:54:52.013+0800: 190009.414: [JNI Weak Reference, 0.0000230 secs], 0.0023470 secs]2019-07-17T21:54:52.013+0800: 190009.414: [class unloading, 0.0198310 secs]2019-07-17T21:54:52.033+0800: 190009.434: [scrub symbol table, 0.0100610 secs]2019-07-17T21:54:52.043+0800: 190009.444: [scrub string table, 0.0011130 secs] [1 CMS-remark: 1259224K(2097152K)] 1282310K(3984640K), 0.1204410 secs] [Times: user=0.48 sys=0.02, real=0.12 secs]2019-07-17T21:54:52.067+0800: 190009.468: Total time for which application threads were stopped: 0.1293860 seconds2019-07-17T21:54:52.068+0800: 190009.468: [CMS-concurrent-sweep-start]2019-07-17T21:54:52.747+0800: 190010.147: [CMS-concurrent-sweep: 0.679/0.679 secs] [Times: user=1.24 sys=0.09, real=0.68 secs]2019-07-17T21:54:52.747+0800: 190010.147: [CMS-concurrent-reset-start]2019-07-17T21:54:52.753+0800: 190010.153: [CMS-concurrent-reset: 0.006/0.006 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]12345678910111213141516复制代码类型:[java] 通用字段解析
CMS-initial-mark
CMS-concurrent-mark
CMS-concurrent-abortable-preclean
YG occupancy
CMS-remark
四、实战1、测试代码/** * 堆溢出测试 * -Xms100m -Xmx100m -XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dump.hprof * * @author Horace */public class HeapOOMTest {private static Logger logger = LoggerFactory.getLogger(HeapOOMTest.class);public static void main(String[] args) {int _1M = 1024 * 1024; List<byte[]> bytes = new ArrayList<>();while (true) { bytes.add(new byte[_1M]); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000)); } } }1234567891011121314151617复制代码类型:[java] 2、查看GC情况本地环境界面查看工具 jvisualvm 每1秒输出一次GC情况 jstat -gccause pid 1s 以下是GC情况 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC 89.83 0.00 42.05 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 46.58 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 51.00 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 54.74 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 58.49 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 63.69 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 67.44 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 71.19 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 75.71 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 79.46 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 83.98 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 87.72 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC 89.83 0.00 92.14 37.10 94.37 90.41 2 0.025 0 0.000 0.025 Allocation Failure No GC1234567891011121314复制代码类型:[java] 各个字段表示的含义可以查看: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html#BEHHGFAE 命令行dump内存快照 jmap -heap dump:live,format=b,file=/tmp/dump.hprof dump出来的内存可以通过jprofile、mat、jhat等工具进行分析 |
|