JVM GC日志分析

        之前一篇博客对GC日志做了简单分析,主要偏重算法的说明,这次作为补充说明,偏重一点GC日志的分析说明及查看GC日志工具的使用,工具有多种,找到自己使用较顺手的一种即可。


GC(Garbage Collection),即垃圾回收机制。目前主流的JVM(HotSpot)采用的是分代收集算法。

垃圾回收算法见博客:https://blog.csdn.net/m0_37568814/article/details/82829660

主要有:标记/清除算法、复制算法、标记/整理算法、分代收集算法。

其中前三种算法比较如下:

效率:复制算法>标记/整理算法>标记/清除算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。
内存整齐度:复制算法=标记/整理算法>标记/清除算法。
内存利用率:标记/整理算法=标记/清除算法>复制算法。

基于上述三种方法的优缺点,分代收集算法应运而生。也是主流的JVM(HotSpot)使用的算法。

  • 分代收集算法:目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。

 内存被分为下面三个区域:
(1)新生代(Eden、from survivor space、to survivor space):空间通常较小,GC发生较频繁、迅速高效;经历过几次新生代的垃圾回收之后,存活下来的对象会被拷贝到老年代的堆空间中。
(2)老年代:比新生代的堆空间要大;内存占用的增长比较慢;GC操作不是很频繁,但是耗时比新生代中的GC要长。
(3)永久代:方法区(JDK8中无永久代了),用来存放类常量和字符串常量。Java 8将java类部分放到java heap里,将字符串常量和类中的静态变量放到内存里面。

  • 垃圾收集区域如下图

 各区域详情描述参见博客,这里不累述:https://blog.csdn.net/m0_37568814/article/details/82873111

  • HotSpot虚拟机所有收集器

1.图中展示了7种不同分代的收集器:   
Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、 CMS、G1

2.它们所处区域,表明其属于新生代收集器还是老年代收集器:
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:Serial Old、Parallel Old、CMS   
整堆收集器:G1;
 
3.两个收集器间有连线,说明它们可以搭配使用(新生代和老年代搭配):       
Serial/Serial Old、Serial/CMS、
ParNew/Serial Old、ParNew/CMS、
Parallel Scavenge/Serial Old、
Parallel Scavenge/Parallel Old、G1;
 
4.其中Serial Old作为CMS出现"Concurrent Mode Failure"失败的后备预案。

  • 进程及堆信息查看

查看进程:jps -l,之后通过 jmap -heap pid 查看堆的概要信息。

在项目的Application配置项VM中配置如下参数,即可在指定目录打印GC日志。

相关配置:
-Xms256m
-Xmx1024m
-Xloggc:/home/work/log/gc/browserApi_gc.log
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails

相关说明:
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 1073741824 (1024.0MB) # 堆最大空闲 jvm参数 -Xmx 1024 m
NewSize = 89128960 (85.0MB) #年轻代空间
MaxNewSize = 357564416 (341.0MB) #年轻代最大空间
OldSize = 179306496 (171.0MB) #老年代空间=堆内存大小-年轻代大小
NewRatio = 2 #Old Generation是 Yong Generation的2倍,即Yong Generation占据内存的1/3
SurvivorRatio = 8# 年轻代内存又被分成三部分 Eden 空间 80% 而From Survivor 空间 和 To Survivor空间 分别占用10%
MetaspaceSize = 21807104 (20.796875MB)#设置元空间的最大值 jvm参数 -XX:MaxMetaspaceSize
CompressedClassSpaceSize = 1073741824 (1024.0MB) # 类指针压缩空间大小, 默认为1G
MaxMetaspaceSize = 17592186044415 MB# 是分配给类元数据空间的最大值
G1HeapRegionSize = 0 (0.0MB)# G1区块的大小. 其取值范围通常为1-32M,要根据最小Heap大小划分出2048个区块

相关分析如下:

简单总结:
PS Young Generation 年轻代空间,使用量达到 33.6%,内存剩余45.81MB,当后续执行程序过程中占用内存超过剩余内存时,会触发Eden Space 空间(年轻代) Minor GC (年轻代垃圾收集)。
PS Old Generation老年代空间,使用量达到 64.2%,内存剩余200.55MB,若后续执行程序过程中占用内存超过年轻代剩余空间,会占用老年代一部分内存;若老年代空间仍然不够用,则会触发老年代空间的Major GC(Full GC,老年代垃圾收集),否则不会触发老年代空间 Major GC。

相关日志说明如下:

2019-03-13T16:28:14.754+0800: 8.608: [GC (Allocation Failure) [PSYoungGen: 262425K->58854K(287744K)] 312148K->113331K(549888K), 0.0140872
secs] [Times: user=0.13 sys=0.00, real=0.02 secs]
2019-03-13T16:28:15.962+0800: 9.815: [GC (Allocation Failure) [PSYoungGen: 287718K->61417K(178176K)] 342195K->272935K(440320K), 0.0628261
secs] [Times: user=0.15 sys=0.02, real=0.06 secs]
2019-03-13T16:28:16.024+0800: 9.878: [Full GC (Ergonomics) [PSYoungGen: 61417K->0K(178176K)] [ParOldGen: 211518K->247771K(572928K)] 27293
5K->247771K(751104K), [Metaspace: 57013K->57013K(1101824K)], 0.0706490 secs] [Times: user=0.52 sys=0.00, real=0.07 secs]
2019-03-13T16:28:16.671+0800: 10.525: [GC (Allocation Failure) [PSYoungGen: 116736K->97656K(232960K)] 364507K->345436K(805888K), 0.019432
7 secs] [Times: user=0.05 sys=0.00, real=0.02 secs]

说明:图片来自博客https://segmentfault.com/a/1190000010648021

GC日志详细分析如下:

1. Parallel Scavenge 是年轻代 GC 收集器
2019-03-13T16:28:14.754+0800: 8.608: [GC (Allocation Failure) ...
2019-03-13T16:28:14.754+0800: 8.608:是本次GC发生的时间,从jvm启动起开始计时,单位为秒。这是一次Minor GC(年轻代垃圾收集),Minor GC 非常频繁,回收速度快。
格式为:[PSYoungGen: a->b(c)]
年轻代使用的是多线程垃圾收集器 Parallel Scavenge(新生代收集器,一般采用复制算法,并行的多线程收集器)
PSYoungGen,表示 GC发生在年轻代。
a 为GC前年轻代已占用空间,年轻代又细分为一个Eden 空间和From Survivor 空间 和 To Survivor空间。
b 为 Minor GC之后Eden空间GC后年轻代已占用空间 或者Survivor中已被占用的空间。
c 括号里的c表示整个年轻代的大小。

2.老年代占用内存空间 计算方式
老年代的内存大小 = (等于) 堆内存总大小 - (减去)年轻代内存大小。
此例中就是 549888K - 287744K = 262144K

3. Parallel Old 是Parallel Scavenge 收集器的老年代版本
老年代GC 又称为Major GC,经常会伴随一次Minor GC(年轻代垃圾回收)速度比较慢
2019-03-13T16:28:16.024+0800: 9.878:是本次GC发生的时间,从jvm启动起开始计时,单位为秒。[Full GC (Ergonomics) ,表示执行全局垃圾回收。
[Metaspace: 57013K→57013K(1101824K)]:java8 特性是把永久代 (Permanent Generation (PermGen)) 移植到元空间(Metaspace)
JDK8 HotSpot JVM 使用本地内存来存储类元数据信息并称之为:元空间(Metaspace

什么时候Full GC:

Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

总结:老年代空间只有在新生代对象转入及创建大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:
java.lang.OutOfMemoryError: Java heap space
为避免以上两种状况引起的Full GC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

下载GCViewer的jar包,打开gc日志:

GCViewer包下载地址:https://download.csdn.net/download/qq_20576739/10826097

 In the total line sum (%) means the percentage of the total pause that was spent for this group of events (95.1% for all "Gc pauses" and 4.9% for all "Full gc events").

相关参考链接及书本:

JVM运行时数据区域:https://blog.csdn.net/m0_37568814/article/details/82873111
Java垃圾收集算法:https://blog.csdn.net/hp910315/article/details/50937045
堆内存使用分析,GC日志详细解读:https://segmentfault.com/a/1190000010648021
JVM垃圾收集器及GC日志相关:https://blog.csdn.net/m0_37568814/article/details/82829660
GCViewer详解:https://www.cnblogs.com/o-andy-o/p/4058271.html
GCViewer 官网:https://github.com/chewiebug/GCViewer

推荐书籍:《深入理解Java虚拟机》(周志明 著)


 相关报错:

1. jps命令报错

mi@mi-OptiPlex-7060:/home/work/log/gc$ jps -l
程序 'jps' 已包含在下列软件包中:
 * openjdk-8-jdk-headless
 * openjdk-9-jdk-headless
请尝试:sudo apt install <选定的软件包>

解决方法:

# 将jps的默认安装路径改为jdk下jps的路径即可,绿色部分修改为java安装的目录
sudo update-alternatives --install /usr/bin/jps jps  /home/mi/software/jdk1.8.0_191/bin/jps 1

注:update-alternatives  --install link name path priority;其中link为系统中功能相同软件的公共链接目录,比如/usr/bin/jps(需绝对目录);name为命令链接符名称,如java path为你所要使用新命令、新软件的所在目录; priority为优先级,当命令链接已存在时,需高于当前值,因为当alternative为自动模式时,系统默认启用priority高的链接;# 整数 根据版本号设置的优先级(更改的优先级需要大于当前的).

详解请见:https://jingyan.baidu.com/article/ff42efa92a64c9c19e2202d2.html

2. Caused by: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 25.191-b12. Target VM is 25.152-b26

原因:是由于机器上安装了多个jdk导致的。所以使用时要指定路径  :/home/mi/software/jdk1.8.0_191/bin/jmap

报错:Can't attach to the process: ptrace(PTRACE_ATTACH, ..) failed for 6623: 不允许的操作

原因:这是因为新版的Linux系统加入了 ptrace-scope 机制. 这种机制为了防止用户访问当前正在运行的进程的内存和状态, 而一些调试软件本身就是利用 ptrace 来进行获取某进程的内存状态的(包括GDB),所以在新版本的Linux系统, 默认情况下不允许再访问了. 可以临时开启. 如:

 

echo 0 > /proc/sys/kernel/yama/ptrace_scope

永久写到文件来持久化:

 

 

 

emacs /etc/sysctl.d/10-ptrace.conf

添加或修改为以下这一句:(0:允许, 1:不允许)

kernel.yama.ptrace_scope = 0

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值