今天整理的这篇文章,整理自之前记录的平常笔记。java
刚开始接触JVM时,对待GC的日志,是保持"逃避"态度的,线上部署的程序没有达到指望的运行效率,或是预期目标时,每每会把问题的矛头指向内存上。程序员
是否是内存不够?算法
GC日志怎么这么多?占用磁盘高达几个G?sql
不想看到打印怎么多,甚至暴力的将程序日志屏蔽掉。安全
我也曾经这样干过,写过的程序,一样致使过内存溢出,甚至是主机宕机。性能优化
是的,java中的垃圾回收的确帮咱们省了不少事,咱们不像C/C++程序员同样,bash
须要考虑分配(malloc)内存分配与(free)释放内存,但我相信每一个java开发的小伙伴都微信
会遇到GC问题,不管是在程序的性能优化,仍是故障分析上。网络
咱们应该在一次又一次的失误中得到经验和教训,而不是去逃避或是草草解决问题了事。 一样的知识点,回顾两次,就会产生 1 + 1 > 2 的效应 。app
因此这篇文章咱们来谈谈耳熟能详的JVM的 GC日志。
首先来讲一说GC的概念, GC 就是垃圾回收 (Garbage Collection) 的缩写。
何为垃圾?
没有任何引用指向的对象,被JVM视为垃圾。
固然,对应的断定算法有:引用计数算法、可达性分析算法。 对应的回收算法有:标记清除、复制、标记整理、分代收集算法。
那GC的日志是什么呢?
GC日志是java虚拟机产生的一种描述性的文本日志。就像咱们开发java程序须要输出log日志同样。JAVA 虚拟机用GC日志来描述,垃圾回收的状况。
Minor GC,表示新生代GC,指发生在新生代的垃圾收集动做,全部的Minor GC都会触发全世界的暂停(stop-the-world),中止应用程序的线程,不过这个过程很是短。
Major GC:老年代GC,指发生在老年代的 GC,也称之为 Full GC。
经过GC日志,咱们能直观的看到内存清理的基本工做过程。
了解垃圾回收的一些行为,什么时候在Young(年轻代)什么时候在回收Old(老年代),而且展现垃圾回收使用到多少资源。
尽管如今Java程序的可视化监控工具已经不少了【介绍?】,可是 GC 日志对于开发人员来讲,是咱们快速定位潜在的内存故障和性能瓶颈最直观的信息之一。
遇到线上GC问题时的日志也是很宝贵的分析素材,这里的GC Allocation Failure图片引用自一位群友的ES节点GC日志。
GC Allocation Failure 是咱们常常遇到的一种GC日志。
分配失败表明着在JVM的Eden区中没有更多的空间来分配对象了,这是minor GC的正常日志。
那别光说不练,那咱们来看看GC Allocation Failure的日志内容是什么样的。
是否是感受很乱?【抠脑壳图 对话】
不要紧,咱们依次来分析分析。
咱们按时间分割,能够知道,截图中一共有两行日志,先来看日志一:
2020-03-17T19:03:19.701+0800: 6664.686:
Total time for which application threads were stopped: 0.0313360 seconds
, Stopping threads took: 0.0000925 seconds
复制代码
毋庸置疑,首先映入眼帘的是带时区的日志时间。
其次 Total time for which application threads were stopped 表示 全部应用线程暂停了 0.0313360 秒。
其中等待全部应用线程到达 【安全点】 用了0.0000925 秒。
暂停的这段时间,其实就是花在了GC上面。后续第二行的 real=0.03 secs 和这里相对应。
刚才提到了 安全点 ,那简单提一提,咱们知道在 Java 的线程有几个不一样的状态。也知道线程若是被 “打断” 会出现什么样的问题。因此对于设计者来讲,须要让线程 “跑” 到安全点上,再停顿。当处于安全点时,线程的状态是肯定的,这样JVM就能够安全的进行一些操做,好比执行垃圾回收。
安全点的位置包括:
若是有线程一直没有进入到安全点,就会致使GC时JVM停顿时间延长。
再来看日志二:
1) 2020-03-17T19:03:20.118+0800: 6665.102:
2)[GC (Allocation Failure) 2020-03-17T19:03:20.118+0800: 6665.102:
3)[ParNew Desired survivor size 8716288 bytes, new threshold 6 (max 6)
4)- age 1: 6826872 bytes, 6826872 total
5)- age 2: 1060216 bytes, 7887088 total
6): 149828K->8895K(153344K), 0.0361997 secs]
7)6272826K->6139400K(8371584K),0.0363166 secs]
8) [Times: user=0.07 sys=0.00, real=0.03 secs]
复制代码
第一行,为日志输出的时间。
第二行,代表了进行了一次 GC 回收,注意,因为这里没有 Full 关键字 ,代表是一次 Minor GC,并指明了 GC 的时间。 Allocation Failure则表示GC的缘由是在年轻代中没有了足够的空间来存储数据了。
第三行,ParNew 一样指明了本次 GC 是发生在年轻代,而且使用的是ParNew垃圾收集器。该收集器采用复制算法回收内存,期间会中止其余工做线程,即Stop The World。
第3、4、五行,表示每次年轻代 GC 以后打印 survivor 区域内对象的年龄分布, threshold则表示设置的晋升老年代的年龄阈值为6。
第六行,分别表示GC前年轻代的使用容量,GC 后该区域使用容量,括号内是该区域的总容量。最后是该内存区域GC耗时,单位是秒。
第七行,分别表示堆内存在垃圾回收之间的大小、堆内存在垃圾回收以后的大小,堆区的总大小。
能够看到在 GC 后,回收对象占比不多。
第八行,显示三个耗时,分别是用户态耗时、内核态耗时、总耗时。
从以上信息咱们能够分析得出如下结论:
本次 GC 新生代减小了: 149828 - 8895 = 140933K。
堆内存区域共减小了: 6272826 - 6139400 = 133426K。
再把两个等号后的结果相减: 140933 - 133426 = 7507K
说明该次共有7507K(7.3M)内存从年轻代移到了老年代,能够看出来数量并很少,说明都是生命周期短的对象,只是这种对象有不少。
咱们须要的是尽可能避免Full GC的发生,让对象尽量的在年轻代就回收掉,因此这里能够稍微增长一点年轻代的大小,让那 7.3M 的数据也保存在年轻代中。
上面介绍了 年轻代的GC日志,下面咱们来讲一说 老年代的 GC 日志 ,其实和年轻代分析的方法同样。
仍是先列出日志:
[gc][238384] overhead, spent [2.2s] collecting in the last [2.3s]
[2020-03-18T01:01:29,020][INFO ][o.e.m.j.JvmGcMonitorService]
[eS] [gc][old][238385][160772] duration [5s],
collections [1]/[5.1s], total [5s]/[4.4d], memory [945.4mb]->[958.5mb]/[1007.3mb],
all_pools {[young] [87.8mb]->[100.9mb]/[133.1mb]}{[survivor] [0b]->[0b]/[16.6mb]}{[old] [857.6mb]->[857.6mb]/[857.6mb]}
复制代码
直接来解释一下吧。 第一行指明了这是第 238384次 GC 在最近 2.3 s 内花了 2.2s 用来作垃圾收集。
相信解读过年轻代的GC,理解第二行的含义并不复杂了。
[gc][本次是 old GC][这是第228385次 GC 检查][从 JVM 启动至今发生的第 160772次 GC] duration [本次检查到的 GC 总耗时 5 秒,多是屡次的加和],
collections [从上次检查至今总共发生1次 GC]/[从上次检查至今已过去 5.1 秒],
total [本次检查到的 GC 总耗时为 5 秒]/[从 JVM 启动至今发生的 GC 总耗时为 4.4 天],
memory [ GC 前 Heap memory 空间]->[GC 后 Heap memory 空间]/[Heap memory 总空间],
all_pools(分代部分的详情)
{[young 区][GC 前 Memory ]->[GC后 Memory]/[young区 Memory 总大小] }
{[survivor 区][GC 前 Memory ]->[GC后 Memory]/[survivor区 Memory 总大小] }{[old 区][GC 前 Memory ]->[GC后 Memory]/[old区 Memory 总大小] }
-XX:+PrintGCDetails 表示打印GC的详细日志
-XX:+PrintGCDateStamps 表示须要展现打印GC的日期时间
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间
日志滚动,输出到指定的日志文件中等等配置。
若是前面的介绍尚未太明白,这里附上两张GC日志的图片,之后遇到须要阅读GC日志的时候,能够用做工具查阅。
年轻代:
老年代:
GC 涉及到的知识点不少,本文可能只是冰山一角,从GC 日志出发分析,让读者了解,GC日志中每一个数字的变化,意味着什么。更多的,先前有介绍过线上 OOM 的一些排查思路和MAT 性能分析工具在实际案例中的使用,同时但愿各位多多结合实际的案例来分析,作好知识储备,便是线上出了 GC 的一些问题也能作到胸有成竹,调整出最优的JVM配置,提升线上程序的运行效率,尽可能避免性能上的一些故障。最后在内容上作个小结
推荐加入java疑难问题攻坚群,推荐阅读