微软开源 GCToolkit,用于解析 GC 日志的工具

微软开源 GCToolkit,用于解析 GC 日志的工具,第1张

出品|开源中国

文|白开水

微软的 Java 工程组宣布开源了 GCToolkit,一组用于分析 Java garbage collection (GC) 日志文件的库。

根据介绍,GCToolkit 工具包将 GC 日志文件解析为离散事件,并提供了一个API 用于从这些事件聚合数据,使用户能够对由垃圾收集日志表示的 Java 虚拟机(JVM)中托管内存的状态进行任意复杂的分析。

GCToolkit 由 3 个 Java 模块组成,涵盖 API、GC 日志文件解析器和基于 Vertx 的 messaging backplane。

其中,API 模块是 GCToolkit 的入口;它把使用解析器和 Vertx 分析 GC 日志文件的细节隐藏在几个方法调用中。解析器模块是一个正则表达式和代码的集合;微软方面称,经过多年的发展,其已经成为最强大的 GC 日志解析器。

基于 Vertx 的 messaging backplane 使用了 2 条消息总线,第一条消息总线来自数据源。当前的实现是从 GC 日志文件流式传输 log lines。这条总线上的监听器是将数据源的数据转换为代表 GC 周期或安全点的事件的解析器。然后这些事件被发布在事件总线上,事件总线上的监听器能够接收和处理他们感兴趣的事件。

GCToolkit 还提供了一个简单的聚合器/聚合框架,用于捕获和分析 GC 日志文件数据。

针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:

一种是部分收集(Partial GC) ,一种是整堆收集(Full GC)。

图1

图2

中括号内: G回收前年轻代堆大小,回收后大小, (年轻代堆总大小)

括号外: GC回收前年轻代和老年代大小,回收后大小, (年轻代和老年代总大小)

GC日志中有三个时间: user, sys和real

由于多核的原因,一般的GC事件中, real time是小于sys + user time的,因为一般是多个线程并发的去做GC,所以real time是要小于systuser time的。<font color=red>如果real>sys+user的话,则你的应用可能存在下列问题: IO负载非常重或者是CPU不够用</font>。

前面打印时间,默认是没有的,需要开启-XX:+PrintGCDateStamps -XX:+PrintGCDetails。

前面打印执行时间,是因为配置了-XX:+PrintGCTimeStamps -XX:+PrintGCDetails

下面对以上日志进行解析:

下面对以上日志进行解析:

例子:

Peak出现溢出前(顶峰)最后一次占用情况的快照。

这里是元空间溢出,可以看见巅峰前,元空间的占用。

老年代oom

回收算法

标记清除算法

根据gcRoot对象的引用链,发现如果该对象没有被引用的情况下,

则标记为垃圾,在清除。

优点:算法简单

缺点:容易产生内存碎片

标记整理算法

根据gcRoot对象的引用链,发现如果该对象没有被引用的情况下,

则标记为垃圾

标记整理与标记清除区别在于:避免标记清除算法产生的碎片问题,

清除垃圾过程中,会将可用的对象实现移动,内存空间更加具有连续性。

优点:没有内存的碎片问题

缺点:整理过程中会产生内存地址移动,效率可能偏低。

标记复制算法

根据gcRoot对象的引用链,发现如果该对象没有被引用的情况下,

将正在被引用的对象拷贝到to区中,让后再直接清理整个from区,在交换位置。

优点:不会产生内存碎片

缺点:比较占内存空间

分代算法

分代算法中主要分成三代: 新生代/from或者 to/老年代

默认 新生代(Young)与老年代(Old)的比例的值为 1:2 (该值可以通过参数–XX:NewRatio 来指定)。

默认的 Eden:from:to=8:1:1 (可以通过参数 –XX:SurvivorRatio 来设定)。

新生代:刚创建的对象都存放在新生代中eden区,当eden区空间内存满之后,则根据GC可达分析算法,将幸存的对象拷贝到to区中,并且寿命+1 如果该对象的寿命>15的情况下

则将该对象放入到老年代中。

老年代:Minor GC新生代GC ,回收多次如果该对象还一直被引用的情况下,则放入到老年代中,如果新生代和老年代内存都满的情况下,则会触发FullGC

总结:

1对象首先分配到eden区(伊田园区) 新生代GC (Minor GC)

2新生代空间不足时,触发Minor GC,将eden区(伊田园区)存活的对象采用标记复制算法放入到to区中,并且该对象的寿命+1

注意:Minor GC因为标记复制算法,会触发stop the world 暂停其他用户的线程,等待垃圾回收结束之后,其他的用户线程才会继续执行。

3如果该对象的寿命>15的情况下,则将该对象放入到老年代中,对象寿命放入在对象头中。

4当老年代空间不足的时候,会触发FullGC 采用标记清理/整理算法

5新生代GC非常频繁,速度比老年代GC要高。

GC相关参数

Stop-The-World

在垃圾回收过程中经常涉及到对对象的挪动(比如上文提到的对象在Survivor 0和Survivor 1之间的复制),进而导致需要对对象引用进行更新。为了保证引用更新的正确性,Java将暂停所有其他的线程,这种情况被称为“Stop-The-World”,导致系统全局停顿。Stop-The-World对系统性能存在影响,因此垃圾回收的一个原则是尽量减少“Stop-The-World”的时间。

回收算法:

误区:没有引用计数算法

1标记清除算法

2标记整理算法

3标记复制算法

回收算法是针对不同的场景使用

按照分代使用

新生代:标记复制算法

老年代:标记清除/标记整理

为什么新生代使用 标记复制算法?而老年代使用标记清除/标记整理

为什么新生代使用 标记复制算法?而老年代使用标记清除/标记整理

因为新生代gc非常频繁,所以选择效率比较高的垃圾回收算法。、

标记清除算法:

优点:算法非常简单。

缺点:空间地址不连续

为什么标记清除算法:空间地址不连续 没有整理

标记整理算法:

优点:空间地址保持连续

缺点:速度慢、会产生移动内存地址 在移动过程中其他线程无法访问堆内存。

标记复制算法:

优点:空间地址保持连续、效率比标记整理算法要高。

缺点: 存在两个空间,占用空间。

分为两个区域:from 和to区 相等。

From和to切换

原理:

当我们堆内存触发gc的时候,在from区中将可用对象拷贝到to中,在直接将整个from

区清空,依次循环切换。

垃圾收集器 并行、串行、cms g1

分代算法:

首先,对我们堆内存空间实现分代,核心分为新生代、老年代

新生代:刚创建的对象一般的情况下都是放在新生代中

分为:eden区() from区 to区

刚创建的对象会放在eden区,当我们新生代eden区空间满的

情况下,会将引用的对象拷贝到to区中,如果gc回收15次的时候

发现该对象还一直被使用的情况下,该对象就会晋升为老年代中。

老生代:

存储空间比例:

1默认的情况下新生代与老年代存储空间比例是1:2

2新生代中eden区与from/to区

为什么新生代中from/to区 比例是1:1

因为新生代中使用的标记复制算法

老年代在什么的情况下会发生fullgc

1在新生代和老年代有堆内存空间都快满的时候。

新生代GCMinorGC

老年代fullgc

当我们老年代gc开始回收垃圾的时候,也会触发新生代gc回收。

老年代在什么时候触发:

当我们老年代空间装不下的时候。

依次内推如何演示新生代gc

Java垃圾收集算法 cms、g1收集器

1标记清除、标记整理、标记复制算法(引用计数法)

1标记清除算法:

优点:算法实现简单

缺点: 内存空间不连续、空间利用率不高 容易产生碎片化

2标记整理算法

优点:空间具有连续性 没有产生碎片化的问题 效率比较低

缺点:对象的应用内存地址有可能会发生变化

3标记复制算法

优点:效率比较高,保证空间连续性的问题

缺点:以空间的方式换时间

分代算法:

分为新生代和老年代

新生代:刚创建的对象都存放到新生代中 ,在新生代中有分成三个区域。

新生代中分为 eden区、from和to区

刚创建的对象存放到eden区,当eden区如果满的时候,幸存的对象晋升为存放到

From或者是to区

新生代触发的GCMinorGC

老年代触发的GCFullGC

那些对象会晋升到老年代中?

1年限达到当gc多次在回收的时候,如果能够一直引用到一个阈值的情况下。直接晋升到老年代中。

2直接是大对象

在默认的情况下 新生代与老年代存储空间比例 是为1:2

在默认的情况下,新生代中 eden区域与from或者是to占比是为8:1:1

程序发生内存溢出:存放的对象空间大小大于我们老年代的空间大小。

因为在整合堆内存中,新生代触发gc回收次数一定比老年代多。

为什么在分代算法中 新生代中有from和to区大小一样。

因为新生代中gc触发非常频繁,为了能够更加高效清理垃圾,所以采用标记复制算法。

垃圾收集器在什么开始触发收集垃圾?

当新生代或者是老年代内存满的情况下,开始触发垃圾

垃圾收集器之前频繁推出垃圾收集器

Stop-The-World 问题

Stop-The-World:当我们垃圾收集器在回收垃圾的时候,

当垃圾回收线程在清理垃圾的时候,会暂停我们的所以用户线程。

导致当前的用户线程短暂卡

在生产环境中调优jvm: 不要频繁触发垃圾回收或者是降低用户线程阻塞的时间

所有的收集器:在回收垃圾的时候都会暂停我们用户的线程

怎样避免触发垃圾收集器频繁回收?

1堆内存空间设置比较大

2堆内存初始化 与最大值一定保持一致。

3生产环境中不要去调用Systemgc();

垃圾收集器 与垃圾收集算法区别:

垃圾收集器:串行、并行收集、CMS、G1 Java11 ZGC收集器,能够降低对我们用户线程暂停的时间或者用户线程和GC线程同时运行

垃圾收集算法:回收算法:标记清除、标记整理、标记复制、分代算法

Stop-The-World

当我们在触发gc回收的时候,有可能会暂停用户的线程。

JVM参数

如果存放的对象空间大于新生代空间,该对象会直接放到老年代中。

GC核心参数

-Xms100 -Xmx

1、-Xms

初始大小内存,默认为物理内存 1/64,等价于 -XX:InitialHeapSize

2、-Xmx

最大分配内存,默认为物理内存的 1/4,等价于 -XX:MaxHeapSize

3、-Xss

设置单个线程栈的大小,一般默认为 512-1024k,等价于 -XX:ThreadStackSize

4、-Xmn

设置年轻代的大小

整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小

持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

5、-XX:MetaspaceSize

设置元空间大小

元空间的本质和永久代类似,都是对 JVM 规范中的方法区的实现。

元空间与永久代之间最大区别:元空间并不在虚拟机中,而是使用本地内存

因此默认情况下,元空间的大小仅受本地内存限制,元空间默认比较小,我们可以调大一点

6、-XX:+PrintGCDetails

输出详细GC收集日志信息

7、-XX:SurvivorRatio

设置新生代中 eden 和 S0/S1 空间比例,默认 -XX:SurvivorRatio=8,Eden : S0 : S1 = 8 : 1 :

8、-XX:NewRatio

配置年轻代和老年代在堆结构的占比,默认 -XX:NewRatio=2 新生代占1,老年代占2,年轻代占整个堆的 1/3

9、-XX:MaxTenuringThreshold

设置垃圾最大年龄

新生代与老年代参数比例设置

-XX:+PrintGCDetails -verbose:gc -XX:SurvivorRatio=2 -XX:NewRatio=1

GC日志参数分析

[GC (Allocation Failure) [PSYoungGen: 1626K->488K(6144K)] 1626K->688K(19968K), 00039387 secs] [Times: user=014 sys=000, real=000 secs]

新生代发生GC ,从堆内存回收前占用:1626k 回收后变为占用488K ,后面表示GC的用时。

[Full GC (Allocation Failure) [PSYoungGen: 488K->0K(6144K)] [ParOldGen: 296K->593K(13824K)] 784K->593K(19968K), [Metaspace: 3099K->3099K(1056768K)], 00043821 secs]

当发生老年代GC时,也会触发新生代GC,新生代堆回收前占用488K,回收后占用0K

,ParOldGen 老年代回收前占用784K,回收后占用593K

GC分析大对象

当我们直接存一个大的对象的时候,比新生代占用空间还有大时,会直接晋升为老年代。

以上就是关于微软开源 GCToolkit,用于解析 GC 日志的工具全部的内容,包括:微软开源 GCToolkit,用于解析 GC 日志的工具、GC日志分析、47 jvm性能优化之GC日志参数分析等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9447107.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-28
下一篇 2023-04-28

发表评论

登录后才能评论

评论列表(0条)

保存