内存飙高问题如何排查?

目录

1、查看日志

2、查看GC情况

3、分析堆内存对象占用情况

4、分析堆内存快照文件


内存飙高如果发生在java进程上,一般情况是因为创建了大量对象导致,持续飙高说明垃圾回收跟不上对象创建的速度,或者内存泄漏导致对象无法被回收!

排查中涉及到如下命令:

jstat -gc pid 1000  查看gc情况,时间等信息,每隔一秒打印一次

jmap -histo pid | head -20 查看堆内存占用空间最大的钱20个对象类型

jmap -dump:live,format=b,file=/home/chenjian/myheapdump.hprof pid  导出堆内存快照

如果每次gc次数频繁,而且每次回收的内存空间也正常,那说明是因为对象创建速度快导致内存一直占用很高;如果每次垃圾回收的内存非常小,那么很可能是因为内存泄漏导致内存一直无法被回收 !

建一个简单的springboot程序,打成jar包,运行:

java -Xmx100m -jar JVMDemo-0.0.1-SNAPSHOT.jar

package com.cjian.jvmdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Array;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: cjian
 * @Date: 2024/2/29 17:27
 * @Des:
 */
@RestController
public class TestController {
    static List<User> userList = new ArrayList<>();

    @GetMapping("/test")
    public String test() {
        for (int i = 0; i < 30; i++) {
            userList.add(new User());
        }
        return "success";
    }

    static class User {
        byte[] b = new byte[1024 * 1024];
    }
}

访问test接口几次后就出现访问失败: 

1、查看日志

日志如下:

通过日志发现程序发生了oom,

2、查看GC情况

使用jstat命令查看gc情况: 

[root@localhost chenjian]# jps -l
10323 jdk.jcmd/sun.tools.jps.Jps
10217 JVMDemo-0.0.1-SNAPSHOT.jar
[root@localhost chenjian]# jstat -gc 10217 1000 5
    S0C         S1C         S0U         S1U          EC           EU           OC           OU          MC         MU       CCSC      CCSU     YGC     YGCT     FGC    FGCT     CGC    CGCT       GCT   
        0.0         0.0         0.0         0.0       6144.0       2048.0      96256.0      96109.4    31232.0    30374.1    3968.0    3632.8     14     0.600     3     0.117     4     0.007     0.724
        0.0         0.0         0.0         0.0       6144.0       2048.0      96256.0      96109.4    31232.0    30374.1    3968.0    3632.8     14     0.600     3     0.117     4     0.007     0.724
        0.0         0.0         0.0         0.0       6144.0       2048.0      96256.0      96109.4    31232.0    30374.1    3968.0    3632.8     14     0.600     3     0.117     4     0.007     0.724
        0.0         0.0         0.0         0.0       6144.0       2048.0      96256.0      96109.4    31232.0    30374.1    3968.0    3632.8     14     0.600     3     0.117     4     0.007     0.724
        0.0         0.0         0.0         0.0       6144.0       2048.0      96256.0      96109.4    31232.0    30374.1    3968.0    3632.8     14     0.600     3     0.117     4     0.007     0.724

输出说明:

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
单位:KB

发现,OC:96256.0      OU:96109.4,老年代几乎已经用完了,而且 gc正常,只是没有回收对象;

3、分析堆内存对象占用情况

使用jmap查看堆中占用内存最大的20个对象:

[root@localhost chenjian]# jmap -histo 10217 | head -20
 num     #instances         #bytes  class name (module)
-------------------------------------------------------
   1:         55050       47757536  [B (java.base@19.0.2)
   2:         51933        1246392  java.lang.String (java.base@19.0.2)
   3:          7474         889368  java.lang.Class (java.base@19.0.2)
   4:         23329         746528  java.util.concurrent.ConcurrentHashMap$Node (java.base@19.0.2)
   5:         10386         559528  [Ljava.lang.Object; (java.base@19.0.2)
   6:          4249         336384  [I (java.base@19.0.2)
   7:          7463         298520  java.util.LinkedHashMap$Entry (java.base@19.0.2)
   8:          8796         281472  java.util.HashMap$Node (java.base@19.0.2)
   9:           211         278784  Ljava.internal.vm.FillerArray; (java.base@19.0.2)
  10:          2940         277704  [Ljava.util.HashMap$Node; (java.base@19.0.2)
  11:           274         257888  [Ljava.util.concurrent.ConcurrentHashMap$Node; (java.base@19.0.2)
  12:          1990         175120  java.lang.reflect.Method (java.base@19.0.2)
  13:          9870         157920  java.lang.Object (java.base@19.0.2)
  14:          3146         151008  java.lang.invoke.MemberName (java.base@19.0.2)
  15:          2604         145824  java.util.LinkedHashMap (java.base@19.0.2)
  16:          2625         105000  java.lang.invoke.MethodType (java.base@19.0.2)
  17:          1579         101056  java.net.URL (java.base@19.0.2)
  18:           312         100024  [C (java.base@19.0.2)

发现有一个Byte类型的对象共占用了47757536 字节,通过一个数据类型难以判断是哪里的代码问题,我们可以使用如下命令来导出堆内存快照:

[root@localhost chenjian]# jmap -dump:live,format=b,file=/home/chenjian/myheapdump.hprof 10217
Dumping heap to /home/chenjian/myheapdump.hprof ...
Heap dump file created [66982293 bytes in 0.415 secs]
[root@localhost chenjian]# ls
JVMDemo-0.0.1-SNAPSHOT.jar  myheapdump.hprof

接着打开jvisualvm工具分析dump文件:

4、分析堆内存快照文件

文件->装入,选择 myheapdump.hprof,

由此可以分析得出是哪里的代码产生的对象导致GC无法回收 !

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值