了解下JVM的GC日志

333 阅读3分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。

1 前言

关于JVM内存区域,相信大家都耳熟能详,无非就堆、栈、方法区那几个。

正常开发工作中,除了使用递归之外,很少会出现栈溢出(StackOverFlow)的情况。因此我们的关注点也就在堆和方法区了。

在JDK8之后,方法区的实现变为了元空间,并且字符串常量池等迁移到了堆中,于是乎看懂堆也就是看懂了JVM内存。排除掉JVM监控工具例如jvisualm等,看GC日志也是看JVM异常的技能之一。

这一期我们就简单讲一下GC日志,以下是本期JVM的配置

-Xms5m
-Xmx1024m
-XX:NewRatio=1
-XX:MetaspaceSize=100m
-XX:MaxMetaspaceSize=100m

2 日志命令

2.1 -XX:+PrintGC

image.png GC (Allocation Failure) : 可以看到是由于分配失败导致的GC,堆的大小=5M,具体是新生代还是老年代发生的GC完全看不出,所以我们需要用另一个命令

2.2 -XX:+PrintGCDetails

image.png

  1. PSYoungGen 代表Parallel Scavenge 新生代收集器。
  2. 第二个框代表新生代大小总共2560K,回收后从2048K减少到512K
  3. 第三个框代表堆大小总共5632k,回收后从2048k减少到825k。
  4. user 此次垃圾回收, 垃圾收集线程消耗的所有CPU时间(Total CPU time).
  5. sys 操作系统调用(OS call) 以及等待系统事件的时间(waiting for system event)
  6. real 应用程序暂停的时间(Clock time)。 由于有些收集器并不是串行的,所以一定程度上real不一定等于user + sys

3 场景

3.1 项目启动场景

-XX:+PrintGCDetails

项目刚启动时,由于我们的Xms和Xmx大小设置不一致,导致多次发生GC,并且新生代和堆的大小一直在增大(看箭头),并且发生了FullGC。

  1. PSYoungGen :新生代用Parallel Scavenge收集器
  2. ParOldGen: 老年代用的Parallel Old收集器
  3. MetaSpace 未发生变化,总的大小为1G左右

image.png

3.2 创建对象过大场景

  1. 定义大对象

image.png

  1. 通过循环创建大对象

  2. GC日志 image.png 可以看到新生代回收的内容大小 基本上等于 堆回收的内容大小,如果差距很大,表明老年代也进行了回收,也就是进行了Full GC

3.3 内存泄漏场景

实际场景中,如果服务调用很慢,除了考虑DB连接,服务器负载,OOM同样也是需要考虑的因素。

可以把OOM分为内存溢出和内存泄漏,内存溢出的常见现象为频繁发生新生代GC,这时候临时处理就比较简单了,增大JVM内存就能马上解决。而如果是内存泄漏,常见现象为频繁发生full GC。我们来模拟下内存泄漏:

  1. 首先定义类变量

  2. 通过ArrayList连接BigData对象使其不被标记,导致不被回收

  3. 查看GC日志 image.png

image.png 因为内存回收不掉,所以由于晋升机制对象会移到老年代。从而发生了FullGC。

多次Full GC 后,发生了OutOfMemoryError

结尾

这一期简单介绍了GC日志,有收获的同学点个赞呗。

下期可能也是这方面的内容,也可能是ELK的东西。