Minor GC vs Major GC vs Full GC

垃圾回收的活动会清理对内存中的不同区域,这些事件一般被称为Minor,Major以及Full GC events。本章我们会讨论这些清理事件的不同之处,当然,这些差别对我们来说并不是最重要的。

通常来说,对我们更有意义的是:应用是否满足了它的SLA,因为用户会监控应用的latency以及throughput。也只有在这个时候,GC events才与此有了关联。而对于GC事件来说,其中最重要的部分是:它们是否将应用 stop了,以及这个stop持续的时间。

Minor,Major以及Full GC这些术语早已被广泛使用,但是暂时对它们没有一个合适的定义,我们首先从对这三种GC events的介绍入手:

Minor GC

从Young space 做垃圾回收,称为Minor GC。这个定义确实比较清晰并且被广泛接受,但如果进一步对此做解释的话,有以下几点值得注意:

  1. 当JVM无法为一个新对象分配空间时(例如,Eden区域快满了),Minor GC一定会被触发。所以如果分配(allocation)操作越频繁,则Minor GC也会越频繁
  2. 在Minor GC 阶段,Tenured Generally会被忽略掉。从Tenured Generation到Young Generation的引用会被认定为GC roots。从Young Generation 到Tenured Generation的引用在mark 阶段会直接被忽略。
  3. 与普遍观点不同的一点是,Minor GC实际上会触发 stop-the-world pauses,暂停application的线程。对大部分应用来说,如果在Eden中,大部分对象被认为是垃圾,并且不会被复制到Survivor 或 Old 空间的话,应用暂停时间的长度基本可以忽略不计。反之,如果大部分年轻的对象并不会被回收,则Minor GC的暂停会花费更多的时间

Major GC vs Full GC

其实对这些属于其实没有一个官方的定义(无论是JVM中还是GC的研究论文中),不过基于我们刚刚介绍的Minor GC来看的话,对Major GC以及Full GC的定义可以简单的描述为:

  1. Major GC:清理Old空间
  2. Full GC:清理整个堆空间(包括Young 以及 Old 空间)

当然,里面具体的过程会更为复杂,而且大部分Major GC是由Minor GC 触发的,所以在很多情况下也不会将它们分开单独讨论。不过相对于辨别一个GC是Major GC还是Full GC,我们更应该关注的是:当前的GC事件是否暂时停掉了应用的线程,还是说可以与应用中的线程同时并发执行?

这个疑问甚至可以被内建于JVM标准工具上,对此,我们来看一个例子以做进一步解释。我们会使用两个不同的工具来跟踪一个运行了Concurrent Mark and Sweep collector(使用参数 -XX:+UseConcMarkSweepGC)的JVM,并比较它们的输出。

首先我们通过jstat的输出查看一下内部情况:

java -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.test.MyApplication

上面的片段是从一个运行了17s的JVM中获取的,我们可以看到12次Minor GC 后,发生了两次Full GC,持续大约一共50ms左右。你也可以通过基于GUI的工具如 jconsole 或 jvisualvm 查看这些输出。

然后我们看一下从同一个JVM里获取的GC输出日志。可以通过 -XX:+PrintGCDetails 开启GC日志:

java -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.test.MyApplication

日志内容可以提供更多相关信息:

3.157: [GC (Allocation Failure) 3.157: [ParNew: 272640K->34048K(306688K), 0.0844702 secs] 272640K->69574K(2063104K), 0.0845560 secs] [Times: user=0.23 sys=0.03, real=0.09 secs]

4.092: [GC (Allocation Failure) 4.092: [ParNew: 306688K->34048K(306688K), 0.1013723 secs] 342214K->136584K(2063104K), 0.1014307 secs] [Times: user=0.25 sys=0.05, real=0.10 secs]

... cut for brevity ...

11.292: [GC (Allocation Failure) 11.292: [ParNew: 306686K->34048K(306688K), 0.0857219 secs] 971599K->779148K(2063104K), 0.0857875 secs] [Times: user=0.26 sys=0.04, real=0.09 secs]

12.140: [GC (Allocation Failure) 12.140: [ParNew: 306688K->34046K(306688K), 0.0821774 secs] 1051788K->856120K(2063104K), 0.0822400 secs] [Times: user=0.25 sys=0.03, real=0.08 secs]

12.989: [GC (Allocation Failure) 12.989: [ParNew: 306686K->34048K(306688K), 0.1086667 secs] 1128760K->931412K(2063104K), 0.1087416 secs] [Times: user=0.24 sys=0.04, real=0.11 secs]

13.098: [GC (CMS Initial Mark) [1 CMS-initial-mark: 897364K(1756416K)] 936667K(2063104K), 0.0041705 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]

13.102: [CMS-concurrent-mark-start]

13.341: [CMS-concurrent-mark: 0.238/0.238 secs] [Times: user=0.36 sys=0.01, real=0.24 secs]

13.341: [CMS-concurrent-preclean-start]

13.350: [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]

13.350: [CMS-concurrent-abortable-preclean-start]

13.878: [GC (Allocation Failure) 13.878: [ParNew: 306688K->34047K(306688K), 0.0960456 secs] 1204052K->1010638K(2063104K), 0.0961542 secs] [Times: user=0.29 sys=0.04, real=0.09 secs]

14.366: [CMS-concurrent-abortable-preclean: 0.917/1.016 secs] [Times: user=2.22 sys=0.07, real=1.01 secs]

14.366: [GC (CMS Final Remark) [YG occupancy: 182593 K (306688 K)]14.366: [Rescan (parallel) , 0.0291598 secs]14.395: [weak refs processing, 0.0000232 secs]14.395: [class unloading, 0.0117661 secs]14.407: [scrub symbol table, 0.0015323 secs]14.409: [scrub string table, 0.0003221 secs][1 CMS-remark: 976591K(1756416K)] 1159184K(2063104K), 0.0462010 secs] [Times: user=0.14 sys=0.00, real=0.05 secs]

14.412: [CMS-concurrent-sweep-start]

14.633: [CMS-concurrent-sweep: 0.221/0.221 secs] [Times: user=0.37 sys=0.00, real=0.22 secs]

14.633: [CMS-concurrent-reset-start]

14.636: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

根据以上日志内容,我们可以看到在多次Minor GC后,一个与Minor GC不同的事件发生了。但是这里我们并没有看见两轮的Full GC 事件,而仅仅是一轮在Old 空间上发生的GC事件,由多个阶段组成:

  1. Initial Mark 阶段,耗时0.0041705,大约4ms。这个阶段是一个stop-the-world 事件,会暂时停止所有应用线程,以做初始的marking
  2. Markup and Preclean阶段与应用里的线程并行执行
  3. Final Remark 阶段,耗时0.00462010,大约46ms,这个阶段也是一个stop-the-world 事件
  4. Sweep 操作会被并行执行,不会停止应用的线程

所以从实际的垃圾回收日志来看,相对于之前jstat里查看到的两轮Full GC,其实真正发生的只有一次清理Old 空间的Major GC。

不过若单从jstat的输出来看,jstat其实已经将你引入了正确的(优化)决策方向,因为它完整的列出了两次stop-the-world事件(第一次4ms,第二次46ms),这两次事件对当时正在运行的线程产生了一共大约50ms的影响。并且jstat的输出已完全隐藏了并行任务的工作。

References:

https://plumbr.io/java-garbage-collection-handbook

JVM垃圾回收(二)- Minor GC vs Major GC vs Full GC的更多相关文章

  1. 二、JVM — 垃圾回收

    JVM 垃圾回收 写在前面 本节常见面试题 本文导火索 1 揭开 JVM 内存分配与回收的神秘面纱 1.1 对象优先在 eden 区分配 1.2 大对象直接进入老年代 1.3 长期存活的对象将进入老年 ...

  2. .Net平台GC VS JVM垃圾回收

    前言 不知道你平时是否关注程序内存使用情况,我是关注的比较少,正好借着优化本地一个程序的空对比了一下.Net平台垃圾回收和jvm垃圾回收,顺便用dotMemory看了程序运行后的内存快照,生成内存快照 ...

  3. Java:JVM垃圾回收(GC)机制

    JVM垃圾回收算法 1.标记清除(Mark-Sweep) 原理: 从根集合节点进行扫描,标记出所有的存活对象,最后扫描整个内存空间并清除没有标记的对象(即死亡对象)适用场合: 存活对象较多的情况下比较 ...

  4. JVM垃圾回收机制GC

    1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的 ...

  5. JVM垃圾回收(GC)

    JVM垃圾回收(GC) 1. 判断对象是否可以被回收 引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收.此方法简单,但无法解决对象相互循环引用的问 ...

  6. JDK分析工具&JVM垃圾回收(转)

    转自:http://blog.163.com/itjin45@126/blog/static/10510751320144201519454/ 官方手册:http://docs.oracle.com/ ...

  7. jvm - 垃圾回收

    jvm - 垃圾回收 注意 : 本系列文章为学习系列,部分内容会取自相关书籍或者网络资源,在文章中间和末尾处会有标注 垃圾回收的意义 它使得java程序员不再时时刻刻的关注内存管理方面的工作. 垃圾回 ...

  8. Java虚拟机垃圾回收:内存分配与回收策略 方法区垃圾回收 以及 JVM垃圾回收的调优方法

    在<Java对象在Java虚拟机中的创建过程>了解到对象创建的内存分配,在<Java内存区域 JVM运行时数据区>中了解到各数据区有些什么特点.以及相关参数的调整,在<J ...

  9. JVM内存管理、JVM垃圾回收机制、新生代、老年代以及永久代

    内存模型 JVM运行时数据区由程序计数器.堆.虚拟机栈.本地方法栈.方法区部分组成,结构图如下所示. JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)程序计数器 ...

  10. 【搞定Jvm面试】 JVM 垃圾回收揭秘附常见面试题解析

    JVM 垃圾回收 写在前面 本节常见面试题 问题答案在文中都有提到 如何判断对象是否死亡(两种方法). 简单的介绍一下强引用.软引用.弱引用.虚引用(虚引用与软引用和弱引用的区别.使用软引用能带来的好 ...

随机推荐

  1. FB面经Prepare: Bipartite a graph

    input friends relations{{1,2}, {2,3}, {3,4}} 把人分成两拨,每拨人互相不认识, 所以应该是group1{1,3}, group2{2,4} 这道题应该是ho ...

  2. FB面经Prepare: Dot Product

    Conduct Dot Product of two large Vectors 1. two pointers 2. hashmap 3. 如果没有额外空间,如果一个很大,一个很小,适合scan小的 ...

  3. JavaIO流——简单对文件的写入及读取(二)

    前文对Io字符流的输入进行了介绍,在这就不再讲了,简单的来写该怎么读取文件内容吧 public static void readFile(String Filename) throws IOExcep ...

  4. Linux之文件权限

    在Linux系统中,root用户基本对于每个文件都有可操作性,但是普通用户可能只能查看特定的文件,这是因为文件存在的权限机制,初步掌握文件的基本权限就操作可以对一些系统文件或者自定义文件有一个操作空间 ...

  5. 用Python3实现的Mycin专家系统简单实例

    from sys import stderr ######################### TRUE = 1 #定义返回值 FALSE = 0 FACT_LENGTH = 9 #'''前提与结论 ...

  6. vue使用v-for时vscode报错 Elements in iteration expect to have 'v-bind:key' directives

    vue使用v-for时vscode报错 Elements in iteration expect to have 'v-bind:key' directives Vue 2.2.0+的版本里,当在组件 ...

  7. 5.JAVA基础复习——JAVA中的static关键字作用与用法

    static关键字: 特点: 1.static是一个修饰符,用于修饰成员.(成员变量,成员函数)static修饰的成员变量 称之为静态变量或类变量. 2.static修饰的成员被所有的对象共享. 3. ...

  8. Git随笔 -- 初始化远程仓库

    1. 新建文件夹(作为本地仓库与之远程仓库关联),进入文件夹空白处右键选择Git Bash(安装程序下载).[或者在开始菜单里找到Git Bash并打开,使用命令进入文件夹:cd 文件夹名称.] 2. ...

  9. %zsy %lqs 随感

    今天是cj的大毒瘤zsy(对对,您说的都对,题目不难的啦,是我太菜啦)出题. 我校选手lqs仍然坚持高水平的发挥,wzy神犇却不在状态. 面对T1sb题(其实干了2h)和T3的原题(我&lqs ...

  10. 复旦高等代数 I(18级)每周一题

    [问题2018A01]  计算下列 $n+1$ 阶行列式的值: $$|A|=\begin{vmatrix} 0 & 1 & 1 & \cdots & 1 \\ 1 &a ...