理解gc机制

jvm内存区域

jdk8.jpg

垃圾收集算法

1. 引用计数法
思想:就是给对象添加一个引用计数器,如果有其他对象引用了该对象,那么就加1,如果其他对象不引用该对象了,那么就减1。如果该对象的引用计数为0了,说明该对象已经没有引用了。
2. 标记清除法
思想:采用可达性算法遍历所有的GC根节点进行标记,统一回收被标记的对象。缺点:产生很多空间碎片、而且标记遍历所有,清除又遍历所有,效率不高。

标记清除算法.png

可达性算法:就是将当前创建对象作为垃圾回收的根节点,然后每次从该根节点遍历出一堆节点,再将这些节点作为根节点再遍历。一直遍历直到终止。就形成了很多条引用链。当一个根节点没有引用链时,则判定该对象是可回收对象。
java中的GC根节点的对象:
① 虚拟机栈(栈帧中的本地变量表)中引用的对象。
② 元空间中类静态属性引用的对象、常量引用的对象。
③ 本地方法栈中JNI引用的对象(Native修饰的方法)。
3. 复制算法
思想:首先将内存区域按照容量划分为两块大小相等的区域。每次使用其中的一块,将那块上面存活的对象复制到另一块上,使用过的那块直接清理掉。如果采用1:1,代价太高,所以默认的Hotspot将Eden和Survivor采用8:1。这种算法适用于新生代,新生代的特点朝生夕灭。
复制算法.png

4. 标记整理算法
思想:将所有存活的对象都向一边移动,最后清理掉边界外边的内存。这种算法适用于老年代,老年代的特点存活率很高。
标记整理算法.png

5. 分代算法
分代收集算法是前几个的综合。在新生代,肯定会有大量的对象被回收,那么就使用复制算法。在老年代存活率较高,被回收的对象较少,那么就使用标记-清除算法或者标记-整理算法。

垃圾收集器


1. 串行回收器 Serial
单线程垃圾回收、采用复制算法、独占式垃圾回收导致STW现象。
2. 并行回收器 ParNew
多线程垃圾回收、采用复制算法、独占式垃圾回收,对于并发能力强的cpu,停顿时间会稍微短些。
3. 新生代并行回收器 ParallelGC
多线程垃圾回收、采用复制算法、关注系统吞吐量。
4. 老年代并行回收器 ParallelOldGC
老年代多线程垃圾回收、采用标记整理算法。
5. CMS回收器
老年代多线程垃圾回收、CMS垃圾收集器关注的是系统停顿时间、采用标记清除算法。
过程:
初始标记 → 并发标记 → 预清理 → 重新标记 → 并发清理 → 并发重置
初始标记:首先初始标记是独占的。主要是根对象关联的对象。
并发标记:遍历GC ROOT,然后标记所有关联对象。
预清理:尝试控制停顿时间。
重新标记:重新标记是独占的。修正并发标记期间,用户程序发生变化而使标记产生变动的那部分记录。
并发清理:将所有没有被引用的对象都清理掉。
并发重置:完成垃圾回收后,重新初始化其结构。
CMS默认启动并发线程数(ParallelGCThreads +3)/4。
6. G1回收器
G1回收器是多个线程工作。G1本身是一个分代收集器。

新生代GC:
    回收Eden区和Survivor区,如果Eden区满了,就会触发新生代GC。Eden区回收清空。一部分对象到Survivor区,还有一部分对象可能直接晋升到老年代。

G1并发标记周期:
    初始标记:标记一下GC Roots能直接关联到的对象。
    根区域扫描:扫描从Survivor区直达老年代区域,标记这些对象(根区域扫描和新生代GC不能同时执行)。
    并发标记:从GC Root开始对堆中的对象进行可达性分析,找出存活对象进行标记。
    重新标记:重新标记也是独占的。修正并发标记期间,用户程序发生变化而使标记产生变动的那部分记录。
    独占清理:该阶段也是独占的,计算各个区域的存活对象和GC回收比例并排序,识别可供混合回收的区域。对这些区域进行标记。
    并发清理:识别并清理所有的空闲区域。并发清理不会引起STW。

混合回收:
     回收时优先选取垃圾比例最高的区域,该阶段叫混合回收。该阶段既会执行正常的年轻代GC,又会选取一些被标记的老年代区域进行回收。

垃圾收集器参数


1. 单线程垃圾收集器
① Serial + Serial Old 参数:-XX:+UseSerialGC
② ParNew + Serial Old 参数:-XX:+UseParNewGC
③ Parallel Scavenge + Serial Old 参数:-XX:+UseParallelGC
2. 多线程垃圾收集器
① ParNew + CMS 参数:-XX:+UseConcMarkSweepGC
② ParNew + Serial Old 参数:-XX:+UseParNewGC
3. CMS垃圾收集器
① ParNew + CMS 参数:-XX:+UseConcMarkSweepGC
4. G1垃圾收集器
① -XX:+UseG1GC

GC日志

虚拟机参数查看日志

1. -XX:+PrintGC

[GC (System.gc())  6093K->688K(125952K), 0.0008452 secs]
[Full GC (System.gc())  688K->583K(125952K), 0.0051261 secs]

说明:
第一行YGC:GC前已使用堆为6093K,GC后已使用堆为688K,YGC发生时可使用堆空间为125952K,整个YGC消耗时间为0.0008452秒。
第二行FGC:GC前已使用堆为688K,GC后已使用堆为583K,FGC发生时可使用堆空间为125952K,整个YGC消耗时间为0.0051261秒。
2. -XX:+PrintGCDetails

[GC (System.gc()) 
[PSYoungGen: 6093K->680K(38400K)] 6093K->688K(125952K), 0.0008458 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc())
 [PSYoungGen: 680K->0K(38400K)]
 [ParOldGen: 8K->583K(87552K)] 688K->583K(125952K), 
[Metaspace: 2679K->2679K(1056768K)], 0.0043123 secs]
 [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 38400K, used 333K [0x00000000d5f80000, 0x00000000d8a00000, 0x0000000100000000)
  eden space 33280K, 1% used [0x00000000d5f80000,0x00000000d5fd34a8,0x00000000d8000000)
  from space 5120K, 0% used [0x00000000d8000000,0x00000000d8000000,0x00000000d8500000)
  to   space 5120K, 0% used [0x00000000d8500000,0x00000000d8500000,0x00000000d8a00000)
 ParOldGen       total 87552K, used 583K [0x0000000081e00000, 0x0000000087380000, 0x00000000d5f80000)
  object space 87552K, 0% used [0x0000000081e00000,0x0000000081e91d58,0x0000000087380000)
 Metaspace       used 2686K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 295K, capacity 386K, committed 512K, reserved 1048576K

说明:

[GC (System.gc()) 
[PSYoungGen: 6093K->680K(38400K)] 6093K->688K(125952K), 0.0008458 secs] 
[Times: user=0.00 sys=0.00, real=0.00 secs]

Parallel Scavenge收集器,新生代收集:YGC前已使用空间为6093K,YGC后已使用空间为680K,新生代总空间为38400K;YGC前堆已使用空间为6093K,YGC后堆已使用空间为688K,堆总空间为125952K。新生代垃圾收集消耗时间0.0008458秒。
消耗用户态cpu时间为0.00秒;消耗内核态cpu时间为0.00秒;垃圾收集器实际消耗时间为0.00秒。

[Full GC (System.gc())
 [PSYoungGen: 680K->0K(38400K)]
 [ParOldGen: 8K->583K(87552K)] 688K->583K(125952K), 
 [Metaspace: 2679K->2679K(1056768K)], 0.0043123 secs]
 [Times: user=0.00 sys=0.00, real=0.01 secs]

Parallel Scavenge收集器,新生代收集:Full GC将新生代的Survivor幸存者区域已使用了680K回收,新生代经过Full GC后已使用了0K。新生代总空间为38400K。
Parallel Old收集器,老年代收集:Full GC前老年代已使用空间为8K,Full GC后老年代已使用空间为583K,老年代总空间为87552K;堆总空间Full GC前已使用688K,堆总空间Full GC后已使用583K,堆总空间为125952K。
Metaspace 元空间收集:Full GC前元空间已使用了2679K,Full GC后元空间已使用了2679K;元空间总空间为1056768K,元空间收集经历了0.0043123秒。
消耗用户态cpu时间为0.00秒;消耗内核态cpu时间为0.00秒;垃圾收集器实际消耗时间为0.01秒。

Metaspace       used 2686K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 295K, capacity 386K, committed 512K, reserved 1048576K

元空间 已使用2686K,容量4486K, 已提交4864K,已保留1056768K

内存回收

Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

对象的分配和晋升

(1)对象生在Eden区:对象首次创建被放置在Eden区。如果容量不够,那么将会报java.lang.OutOfMemoryError错误。
(2)老年对象进入老年代:没经历GC,初创对象不会离开Eden。新生代的对象每经过一次GC,年龄加1,达到15岁就晋升到老年代。调整晋升年龄的参数:MaxTenuringThreshold。
(3)大对象直接进入老年代:新生代的对象很大,Survivor幸存者区无法容纳,那么对象将直接晋升到老年代。


参考
http://ju.outofmemory.cn/entry/362140
https://blog.csdn.net/weixin_39788856/article/details/80388002

你可能感兴趣的:(理解gc机制)