今天2B哥跟各位牛人分享JVM相关的知识点,今天重点介绍CMS和G1收集器,某些小哥哥就问为何不讲讲其余收集器?按面试经验来讲,这两种收集器问的最多,固然优先讲这两种呀,可是,我说可是,若是你关注我还能看到更多关于JVM的知识,保证让你收获满满,废话很少说,直接上干货。java
收集器于JVM堆的关系
复制代码
JVM参数:-XX:+UseConcMarkSweepGC面试
CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工做。-XX:+UseConcMarkSweepGC来开启CMS算法
从名字中的Mark Sweep这两个词能够看出,CMS 收集器是一种 “标记-清除”算法实现的,它的运做过程相比于前面几种垃圾收集器来讲更加复杂一些。整个过程分为四个步骤:bash
初始标记: 暂停全部的其余线程,并记录下直接与gc root 相连的对象,速度很快 ;服务器
并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前全部的可达对象。由于用户线程可能会不断的更新引用域,因此 GC 线程没法保证可达性分析的实时性。因此这个算法里会跟踪记录这些发生引用更新的地方。闭包
从新标记: 从新标记阶段就是为了修正并发标记期间由于用户程序继续运行而致使标记产生变更的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段的时间稍长,远远比并发标记阶段时间短并发
并发清除: 开启用户线程,同时 GC 线程开始对为标记的区域作清扫。性能
-Xmx200M -Xmn50m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseConcMarkSweepGC
复制代码
输出GC日志以下: 测试
第一歩初始化标记
2020-01-05T21:52:05.411+0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 72364K(77824K)] 77550K(123904K), 0.0010600 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第二歩并发标记
2020-01-05T21:52:05.412+0800: [CMS-concurrent-mark-start]
2020-01-05T21:52:05.416+0800: [CMS-concurrent-mark: 0.004/0.004 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第三歩 预清理
2020-01-05T21:52:05.416+0800: [CMS-concurrent-preclean-start]
2020-01-05T21:52:05.417+0800: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第四步 可被终止的预清理
[CMS-concurrent-abortable-preclean-start]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第五歩 从新标记
2020-01-05T21:52:05.417+0800: [GC (CMS Final Remark) [YG occupancy: 5185 K (46080 K)]2020-01-05T21:52:05.417+0800: [Rescan (parallel) , 0.0003800 secs]2020-01-05T21:52:05.417+0800: [weak refs processing, 0.0006763 secs]2020-01-05T21:52:05.418+0800: [class unloading, 0.0009691 secs]2020-01-05T21:52:05.419+0800: [scrub symbol table, 0.0019453 secs]2020-01-05T21:52:05.421+0800: [scrub string table, 0.0004832 secs][1 CMS-remark: 72364K(77824K)] 77550K(123904K), 0.0046503 secs] [Times: user=0.02 sys=0.00, real=0.00
第六歩清理
2020-01-05T21:52:07.174+0800: [CMS-concurrent-sweep-start]
2020-01-05T21:52:07.175+0800: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
第七歩重置
2020-01-05T21:52:07.175+0800: [CMS-concurrent-reset-start]
复制代码
总结CMS收集器 处理过程有7个步骤:spa
主要优势:并发收集、低停顿。
可是它有下面三个明显的缺点:
· 对 CPU 资源敏感;
· 没法处理浮动垃圾;
· 它使用的回收算法-**“标记-清除”**算法会致使收集结束时会有大量空间碎片产生(不作整理 -XX:CMSFullGCsBeforeCompaction=n 多少次以后作压缩)。
JVM参数:-XX:+UseG1GC** **
G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高几率知足 GC 停顿时间要求的同时,还具有高吞吐量性能特征.
被视为 JDK1.7 中 HotSpot 虚拟机的一个重要进化特征。它具有一下特色:
并行与并发:G1 能充分利用 CPU、多核环境下的硬件优点,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿时间。部分其余收集器本来须要停顿 Java 线程执行的 GC 动做,G1 收集器仍然能够经过并发的方式让 java 程序继续执行。
G1将Java堆划分为多个大小相等的独立区域(Region),JVM最多能够有2048个Region**。**
通常Region大小等于堆大小除以2048,好比堆大小为4096M,则Region大小为2M,固然也能够用参数"-XX:G1HeapRegionSize"手动指定Region大小,可是推荐默认的计算方式。
G1保留了年轻代和老年代的概念,但再也不是物理隔阂了,它们都是(能够不连续)Region的集合。
默认年轻代对堆内存的占比是5%,若是堆大小为4096M,那么年轻代占据200MB左右的内存,对应大概是100个Region,能够经过“-XX:G1NewSizePercent”设置新生代初始占比,在系统运行中,JVM会不停的给年轻代增长更多的Region,可是最多新生代的占比不会超过60%,能够经过“-XX:G1MaxNewSizePercent”调整。年轻代中的Eden和Survivor对应的region也跟以前同样,默认8:1:1,假设年轻代如今有1000个region,eden区对应800个,s0对应100个,s1对应100个。
一个Region可能以前是年轻代,若是Region进行了垃圾回收,以后可能又会变成老年代,也就是说Region的区域功能可能会动态变化。
G1垃圾收集器对于对象何时会转移到老年代跟以前讲过的原则同样,惟一不一样的是对大对象的处理,G1有专门分配大对象的Region叫Humongous区,而不是让大对象直接进入老年代的Region中。在G1中,大对象的断定规则就是一个大对象超过了一个Region大小的50%,好比按照上面算的,每一个Region是2M,只要一个大对象超过了1M,就会被放入Humongous中,并且一个大对象若是太大,可能会横跨多个Region来存放。
Humongous区专门存放短时间巨型对象,不用直接进老年代,能够节约老年代的空间,避免由于老年代空间不够的GC开销。
Full GC的时候除了收集年轻代和老年代以外,也会将Humongous区一并回收。
-Xmx200M -Xmn50m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseG1GC
复制代码
输出日志以下:
2020-01-08T13:19:36.126+0800: [GC pause (G1 Humongous Allocation)
第一步初始化标记
(young) (initial-mark), 0.0023830 secs]
[Parallel Time: 1.1 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 162.8, Avg: 162.8, Max: 162.9, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.3, Avg: 0.5, Max: 0.9, Diff: 0.6, Sum: 3.8]
[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.4, Max: 0.5, Diff: 0.5, Sum: 2.8]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.2]
[Termination Attempts: Min: 1, Avg: 7.3, Max: 11, Diff: 10, Sum: 58]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.3]
[GC Worker Total (ms): Min: 0.9, Avg: 0.9, Max: 0.9, Diff: 0.0, Sum: 7.2]
[GC Worker End (ms): Min: 163.7, Avg: 163.7, Max: 163.7, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.6 ms]
[Other: 0.7 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.5 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 3072.0K(50.0M)->0.0B(49.0M) Survivors: 0.0B->1024.0K Heap: 30.5M(126.0M)->28.8M(126.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
2020-01-08T13:19:36.129+0800: [GC concurrent-root-region-scan-start]
2020-01-08T13:19:36.135+0800: [GC concurrent-root-region-scan-end, 0.0060575 secs]
第二步初始化标记
2020-01-08T13:19:36.135+0800: [GC concurrent-mark-start]
2020-01-08T13:19:36.135+0800: [GC concurrent-mark-end, 0.0000731 secs]
2020-01-08T13:19:36.135+0800: [GC remark 2020-01-08T13:19:36.135+0800: [Finalize Marking, 0.0002335 secs] 2020-01-08T13:19:36.136+0800: [GC ref-proc, 0.0000731 secs] 2020-01-08T13:19:36.136+0800: [Unloading, 0.0005882 secs], 0.0011010 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
第三步筛选回收
2020-01-08T13:19:36.137+0800: [GC cleanup 49M->49M(126M), 0.0007633 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
复制代码
*初始标记(initial mark,STW):暂停全部的其余线程,并记录下gc roots直接能引用的对象,速度很快 ;
*并发标记(Concurrent Marking):同CMS的并发标记最终标记(Remark,STW):同CMS的从新标记
*筛选回收(Cleanup,STW):筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所指望的GC停顿时间(能够用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划,好比说老年代此时有1000个Region都满了,可是由于根据预期停顿时间,本次垃圾回收可能只能停顿200毫秒,那么经过以前回收成本计算得知,可能回收其中800个Region恰好须要200ms,那么就只会回收800个Region,尽可能把GC致使的停顿时间控制在咱们指定的范围内。这个阶段其实也能够作到与用户程序一块儿并发执行,可是由于只回收一部分Region,时间是用户可控制的,并且停顿用户线程将大幅提升收集效率。不论是年轻代或是老年代,回收算法主要用的是复制算法,将一个region中的存活对象复制到另外一个region中,这种不会像CMS那样回收完由于有不少内存碎片还须要整理一次,G1采用复制算法回收几乎不会有太多内存碎片。
G1能充分利用CPU、多核环境下的硬件优点,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间
缺点: G1从总体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于**“复制”算法实现**的, 对内存使用率存在必定的“浪费”。
能看到这的都是牛人,麻烦帮忙点个赞关注下,下篇我继续带来CMS和G1实战PK对比,图形化对比看的更直观。
加关注,不迷路。
复制代码