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. CentOS 7 安装MySQL5.7.25

    STEP 1. 下载 去往官方下载MySQL包.http://dev.mysql.com mysql-5.7.25-linux-glibc2.12-x86_64.tar.gz [root@study ...

  2. Ansible 批量管理Windows Server服务器

    Ansible批量管理Windows Server         Ansible是一款为类Unix系统开发的自由开源的配置和自动化工具,  它用Python写成,类似于saltstack和Puppe ...

  3. 多线程之CEvent

    最近工作中要维护一个windows模块,用到了mfc中的CEvent类.这算是很久很久以前的老朋友了吧,估计和我超过10年没见过面了,不过工作就是工作,技术上来不得半点含糊,所以还是重新认识一下这位老 ...

  4. github文件上传与下载

    一.文件上传 ①.注册并登陆github,进入Github首页,点击New repository新建一个项目. ②.填写相应信息后点击create即可 Repository name: 仓库名称 De ...

  5. mysql 事物没提交导致事物一直运行解决方案

    1.设置 innodb_kill_idle_transaction 参数, 可以永久避免 https://dbaplus.cn/news-11-974-1.html

  6. CCF CSP 201812-1 小明上学

    题目链接:http://118.190.20.162/view.page?gpid=T80 问题描述 试题编号: 201812-1 试题名称: 小明上学 时间限制: 1.0s 内存限制: 512.0M ...

  7. CCF CSP 201512-1 数位之和

    题目链接:http://118.190.20.162/view.page?gpid=T37 问题描述 试题编号: 201512-1 试题名称: 数位之和 时间限制: 1.0s 内存限制: 256.0M ...

  8. 叮咚,你的Lauce上线了!

    哈,2014 - 2016 - 2018,虽然每隔两年才有那么一篇随笔,博客园,我还是爱你的~ 嗯,2018,马上又要失业了,我这是自带黑属性啊啊啊哈,工作了4年多的项目要被砍掉了, 倒不是说非要这个 ...

  9. Angular ( 一 ) angular的安装

    1. 全局安装angular 脚手架工具 npm install -g @angular/cli 2. 打开到创建目录: 3. 创建项目 ng new my-app 4. 打开项目 5. 安装依赖 n ...

  10. JS,JQuery小知识

    http://blog.163.com/wumingli456@126/blog/static/28896414201112252456459/?suggestedreading&wumii