乐趣区

关于springboot:JVM频繁GC内存溢出排查

前言
GC(Garbage collection)频繁和堆内存溢出起因简略来说是对象占用堆空间难以回收,新对象无奈调配触发 GC 或者间接导致内存溢出,最终过程完结。
排查思路是先查看过程各种类型对象占用空间大小和比例,锁定占用空间较多的对象后再剖析相干的程序是否有使用不当的中央。下文的侧重点是通过多种形式查看堆内存散布。
例子程序
先编译(javac FrequentFullGCSample.java)例子程序,执行上面的指令来创立 JVM 过程。
指定 -Xms2M -Xmx2M 来限度堆内存大小为 2 兆,作为例子程序够用了;为了不便演示咱们再加上这两个选项:-XX:+PrintCommandLineFlags 打印有变动的 JVM 选项值;-XX:+PrintGCDetails 打印垃圾回收的详细信息。
java -Xms2M -Xmx2M -XX:+PrintCommandLineFlags -XX:+PrintGCDetails FrequentFullGCSample
复制代码
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;

public class FrequentFullGCSample {

   private static final Queue<Object> QUEUE = new LinkedList<>();

   public static void main(String[] args) throws InterruptedException {
       while (true) {
           QUEUE.offer(new Object());
           TimeUnit.MILLISECONDS.sleep(3);
      }
  }
}
复制代码
从控制台日志能够看到首先输入了局部有变动的 JVM 选项值:初始堆(-XX:InitialHeapSize)和最大堆(-XX:MaxHeapSize)大小都为 2097152 字节(B),也就是 2 兆(M);应用了类指针压缩(-XX:+UseCompressedClassPointers)和对象指针压缩(-XX:+UseCompressedOops)。
在前面咱们能够看到输入的 GC 日志信息,对象先调配在年老代,空间占满开始频繁 YGC(Young garbage collection)。

通过几代回收之后,对象分代年龄增长转移到老年代,老年代空间逐步占满开始频繁 Full GC。

因为例子程序新创建的对象都是强援用类型,不能定为垃圾,无奈回收。通过频繁的 Full GC 无果后新对象无奈调配到空间,报错内存溢出(java.lang.OutOfMemoryError)。从前面打印的各个分代的内存应用状况能够看出年老代(def new generation)和老年代(tenured generation)占用比例靠近 100%。

先通过 jps 找到咱们的 JVM 过程 ID

查看堆中对象调配信息
jmap -histo(举荐)
jmap 是 JDK 自带的指令能够查看堆内存相干的信息。jmap -histo <Java 过程 ID> 会列出过程中每个类型对象实例数量和占用空间大小按降序列进去,咱们再加上 head -10 来查看前 10 行信息能够看到排名前几位的对象相干信息。
这种形式适合过程还在运行的时候用,统计输入的信息不多,对过程自身影响比拟小。
jmap -histo 20091 | head -10
复制代码

jmap -dump + Java VisualVM
jmap -dump 能够将堆内存信息以二进制的形式转储到文件,格局为 jmap -dump:format=b,file=< 文件名 > <Java 过程 ID>。
这种形式转储的数据比拟多,当咱们的 JVM 堆内存应用空间比拟大的时候(比方上 G 大小)须要肯定的工夫,而且转储的过程中会影响过程运行。个别不倡议这样做,除非 JVM 配置 / 应用的空间比拟小。
jmap -dump:format=b,file=71976.dump 71976
复制代码

有了堆转储文件,咱们找一个能够剖析这个文件的工具来查看,这里咱们用 JDK 自带的 JVM 图形化工具 Java VisualVM。

Mac 在命令行输入上面命令关上

/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jvisualvm

Windows 在 JDK 中 bin 目录下

复制代码
点击【文件】>【装入】,抉择适合的类型后再选中咱们的堆转储文件关上。

装入后点击类,能够在图形化界面看到过后堆中各种类的实例数和大小。

内存溢出时主动生成堆转储文件(举荐)
启动时减少一个 JVM 选项 -XX:+HeapDumpOnOutOfMemoryError。默认文件会生成到当前目录下,名为 java_pid< 过程 ID>.hprof,能够通过选项 -XX:HeapDumpPath=< 目录 > 生成到指定的目录。再用 Java VisualVM 装入查看即可。
这种形式是比拟举荐的,过程曾经出问题要退出了,退出前能生成信息帮忙咱们预先剖析。
java -Xms2M -Xmx2M \
-XX:+PrintCommandLineFlags \
-XX:+PrintGCDetails \
-XX:+HeapDumpOnOutOfMemoryError FrequentFullGCSample
复制代码
Java VisualVM 近程监控
java 命令中多加几个选项启用近程监控。-Djava.rmi.server.hostname 填本机 IP 地址;-Dcom.sun.management.jmxremote.port 填近程拜访应用的端口;其余两个是为了敞开鉴权不校验用户名明码。
过程开启监控必定须要额定的工作,下发数据、网络交互等等,会影响到过程的运行,所以个别不必。然而能够用在测试的时候,压力测试同时监控,便于剖析。
java -Xms2M -Xmx2M -XX:+PrintCommandLineFlags -XX:+PrintGCDetails \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.port=1099 \
-Djava.rmi.server.hostname=192.168.252.132 FrequentFullGCSample
复制代码
双击【近程】,填好主机地址后点【确定】。

右键点击刚刚配置呈现的主机,抉择【增加 JMX】连贯,填好端口号点【确定】。

双击刚配置的 JMX 连贯,点击【抽样器】>【内存】能够实时看到堆内存中各种对象的分配情况。

总结
以上形式举荐的还是 jmap -histo 和内存溢出时主动转储,对过程自身影响比拟小。
另外 jmap -heap <Java 过程 ID> 能够看堆内存相干的 JVM 配置和堆内存整体调配。

退出移动版