一、前言

今天下午本来在划水,突然看到微信联系人那一个红点点,看了下,应该是博客园的朋友。加了后,这位朋友问了我一个问题:

问我,这两块有什么关系? 看到这段 gc 日志,一瞬间脑子还有点懵,嗯,这个可能要翻下书了,周志明的 Java 虚拟机那本神书里面有讲,我果断地打开了 pdf,找了起来,很快,找到了:

上面发的那个图里,6762k 就是 新生代 gc 前的容量,1006k 就是新生代 gc 后的容量,9216k就是新生代的10m中(8m的eden区+1m的 from survivor区)的大小。

再看后面的那几个数字,6762K-》3455K (19456K),意思就是,GC前,Java堆使用了 6762K,GC后,Java堆使用了 3455K,而19456K就是整个堆的容量(新生代9m+老年代10m)。

按理说,这么解释就足够了,但是,眼尖,他提出了下面的问题(大概意思是这个,我自己配的字):

这他么就尴尬了。。。怎么都解释不通了啊。。。书上怕不是错了啊。。。经过我们一番研究,得出一致结论:

网上搜了下,感觉都是些理论,感觉大家都没问题,就我有问题???想起之前看虎扑帖子,有人发帖说浙江很富,没有穷人(确实富),有浙江网友回复:浙江就我一个穷人!

我现在就是那个感觉,大家都没问题,就我有问题??

当然,如果我就此止步了,也就不会写这个了,我和那位朋友捣鼓了一下,还是理解清楚了 gc 日志。

二、gc 日志 的正确阅读方法

友情提示:大家跑不出来和我一样效果的话,记得看看自己打断点了没。

找这位朋友拿了他的测试代码,很简单的demo,如下:

 import java.util.concurrent.TimeUnit;

 public class AllocationTest {

     public static final int _1MB = 1024 * 1024;

     public static void main(String[] args) throws InterruptedException {
byte[] allocation1, allocation2, allocation3, allocation4; allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
//触发Minor GC
allocation4 = new byte[4 * _1MB];
}
}

JVM参数如下:

 -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

我本地先跑了下,结果是下面这样的:

这个console,和这位朋友的也不一致,我这边连那行 gc 前后的堆大小的日志都没打。因为他发我的参数里,可以看出来,没有指定垃圾收集器,那就是用了默认垃圾收集器。 而默认的垃圾收集器在不同版本的电脑上、不同版本的 JVM 上可能不一致。为了方便统一认识,我就建议大家统一用 Serial new 收集器,于是加了下面参数:

-verbose:gc -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

这次再运行,确实可以打印出来了

这里,只是重现了和这位朋友一样的问题,但问题本身,还没解决.。基于有问题就DEBUG的习惯,在下面这行打了个断点:

 import java.util.concurrent.TimeUnit;

 public class AllocationTest {

     public static final int _1MB = 1024 * 1024;

     public static void main(String[] args) throws InterruptedException {
byte[] allocation1, allocation2, allocation3, allocation4; allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
//触发Minor GC
allocation4 = new byte[4 * _1MB]; 16 TimeUnit.SECONDS.sleep(100);
}
}

在16行打了个断点,断点停在 16 行以后,console 如下:

让我惊醒的是,这次只打了这一行,并没有打印下图这部分:

其实这里已经可以分析出来 gc 的大体过程了,不过为了方便我们理解,我们可以加上以下 JVM 参数:

-verbose:gc -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+PrintGC  -XX:+PrintGCTimeStamps  -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintGCApplicationStoppedTime 

这次,断点依然如约停在了那一行,我们看看 console,首先,下面是 gc 前的堆占用情况

然后,看看 gc 后的情况:

这里,我们就可以看出来了, 那3个 2m 大的对象,一开始占用了 eden区,eden区总共只有8m,那就只剩2m(实际上没剩2m,因为jvm自己占了一部分),这时候,我们要分配一个 4m 大的对象,那,JVM 在收到这个分配 4m 内存的请求后,检查了 自身的 eden区,明显不够,那就 gc 吧,也没啥好gc 的,那三个2m的对象,生命周期还没结束,我们当前的线程堆栈还维持着对它们的强引用,肯定是没法回收了。 3个 2m 的对象,活过本次gc,本来要放到 to survivor 区,但是明显放不下,于是只好丢给 老年代了。

于是,老年代被占用了 6m 空间。

理清了上述过程,再看下面那行gc 日志:

大家可以拿计算器算下,1kb 都没差。6144 + 569 = 6713!有一种做对数学题的快感!

三、其他收集器的表现

我们知道,有很多种收集器组合。

这里,我和大家试验几种,具体大家可以分析下:

1、上图红线组合3  -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

2、红线组合4  -XX:+UseParNewGC

3、 红线组合7,G1收集器

-XX:+UseG1GC  这个表示看不懂了。

更多的垃圾收集器组合,参数及配置,参考:

JVM垃圾收集器组合--各种组合对应的虚拟机参数实践

 
 

三、总结

计算机科学,真是一门实践的学问。道友们,一起加油! 欢迎有兴趣的铜须,加群一起学习!

曹工杂谈:手把手带你读懂 JVM 的 gc 日志的更多相关文章

  1. 【曹工杂谈】Mysql-Connector-Java时区问题的一点理解--写入数据库的时间总是晚13小时问题

    背景 去年写了一篇"[曹工杂谈]Mysql客户端上,时间为啥和本地差了整整13个小时,就离谱",结果最近还真就用上了. 不是我用上,是组内一位同事,他也是这样:有个服务往数据库in ...

  2. 一文带你读懂什么是vxlan网络

    一个执着于技术的公众号 一.背景 随着云计算.虚拟化相关技术的发展,传统网络无法满足大规模.灵活性要求高的云数据中心的要求,于是便有了overlay网络的概念.overlay网络中被广泛应用的就是vx ...

  3. 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

    1.引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本 ...

  4. 【曹工杂谈】Maven源码调试工程搭建

    Maven源码调试工程搭建 思路 我们前面的文章<[曹工杂谈]Maven和Tomcat能有啥联系呢,都穿打补丁的衣服吗>分析了Maven大体的执行阶段,主要包括三个阶段: 启动类阶段,负责 ...

  5. 实战 | 一文带你读懂Nginx反向代理

    一个执着于技术的公众号 前言 在前面的章节中,我们已经学习了nginx基础知识: 给小白的 Nginx 10分钟入门指南 Nginx编译安装及常用命令 完全卸载nginx的详细步骤 Nginx 配置文 ...

  6. 读懂mysql慢查询日志

    我们来看一下如何去读懂这些慢查询日志.在跟踪慢查询日志之前,首先你得保证最少发生过一次慢查询.如果你没有可以自己制造一个:root@server# mysql -e 'SELECT SLEEP(8); ...

  7. 从源码入手,一文带你读懂Spring AOP面向切面编程

    之前<零基础带你看Spring源码--IOC控制反转>详细讲了Spring容器的初始化和加载的原理,后面<你真的完全了解Java动态代理吗?看这篇就够了>介绍了下JDK的动态代 ...

  8. 曹工杂谈--使用mybatis的同学,进来看看怎么在日志打印完整sql吧,在数据库可执行那种

    前言 今天新年第一天,给大家拜个年,祝大家新的一年里,技术突突突,头发长长长! 咱们搞技术的,比较直接,那就开始吧.我给大家看看我demo工程的效果(代码下边会给大家的): 技术栈是mybatis/m ...

  9. 一文带你读懂zookeeper在大数据生态的应用

    一个执着于技术的公众号 一.简述 在一群动物掌管的世界中,动物没有人类聪明的思想,为了保持动物世界的生态平衡,这时,动物管理员-zookeeper诞生了. 打开Apache zookeeper的官网, ...

随机推荐

  1. 开源项目 RethinkDB 关闭,创始人总结失败教训(市场定位错误)

    当我们宣布RethinkDB关闭时,我答应写一个调查分析.我花了一些时间来整理所得的教训和经验,现在可以清楚地写出来. 在HN讨论贴中,人们提出了许多关于为什么RethinkDB失败的原因,从莫名的人 ...

  2. Z Order of Controls in Delphi VCL

    Get and set the Z Order of controls at runtime in Delphi VCL. If you are looking for a FireMonkey so ...

  3. python列表的内置方法

    list数据类型还有更多的方法.这里是list对象的所有方法: list.append(x) 添加一个元素到列表的末尾:相当于a[len(a):] = [x]. list.extend(L) 将指定列 ...

  4. 再说Java集合,subList之于ArrayList

    上一章说了很多ArrayList相关的内容,但还有一块儿内容没说到,那就是subList方法.先看一段代码 public static void testSubList() { List<Str ...

  5. java web 开发教程(1) - 开发环境搭建

    勤拂拭软件系列教程 之 Java Web开发之旅(1) Java Web开发环境搭建 1 前言 工作过程中,遇到不少朋友想要学习jsp开发,然而第一步都迈不出,连一个基本的环境都没有,试问,如何能够继 ...

  6. 机器学习之支持向量机原理和sklearn实践

    1. 场景描述 问题:如何对对下图的线性可分数据集和线性不可分数据集进行分类? 思路: (1)对线性可分数据集找到最优分割超平面 (2)将线性不可分数据集通过某种方法转换为线性可分数据集 下面将带着这 ...

  7. 09 audio和vedio标签

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...

  8. 高并发 Nginx+Lua OpenResty系列(9)——HTTP服务

    此处我说的HTTP服务主要指如访问京东网站时我们看到的热门搜索.用户登录.实时价格.实时库存.服务支持.广告语等这种非Web页面,而是在Web页面中异步加载的相关数据.这些服务有个特点即访问量巨大.逻 ...

  9. 论文研读Unet++

    Unet++: A Nested U-Net Architecture for Medical Image Segmentation Unet++ 论文地址 这里仅进行简要介绍,可供读者熟悉其结构与特 ...

  10. spring 5.x 系列第13篇 —— 整合RabbitMQ (xml配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 本用例关于rabbitmq的整合提供简单消 ...