出品|开源中国
文|白开水
微软的 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日志参数分析等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)