【诊断】linux系统下的内存溢出问题定位

步骤:

        (1)编写并运行一个会造成内存溢出的代码

import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class HeapLeakTest {

    static AtomicInteger i = new AtomicInteger(1);

    public static void main(String[] args) {

        HashMap map = new HashMap<>();
        while (true) {

            map.put(new String("第" + i.get() + "个对象"), i.get());
            i.incrementAndGet();
        }
    }
}

        在死循环中不断创建String对象和Integer对象,map不能及时释放无用对象的内存资源,模拟内存溢出。

        (2)使用top命令查看所有进程使用系统资源的情况

【诊断】linux系统下的内存溢出问题定位_第1张图片

        可以看到9180进程(Java程序)CPU占用率异常。

        (3)使用top -H -p 9180查看进程下每一个线程使用CPU的情况

【诊断】linux系统下的内存溢出问题定位_第2张图片

        可以看到9182、9183、9184、9185、9186、9187、9188、9189等线程的CPU占用率异常。

        (4)通过jstack 9180 > log.txt命令将Java程序的堆栈信息打印到log.txt中,使用cat log.txt | grep 0x23df(线程id的十六进制形式)过滤出异常线程的信息

【诊断】linux系统下的内存溢出问题定位_第3张图片

        我们可以看到,这些CPU使用率异常的线程都是GC线程,那么CPU使用率异常问题的产生原因肯定在堆内存中了。

        (5)通过jstat -gcutil 9180 2000 10命令查看实时堆内存使用情况

【诊断】linux系统下的内存溢出问题定位_第4张图片

        其中FGC(Full GC)次数最高达到137次,FGCT(Full GC Time)总时间为67s,每次Full GC耗时:67 / 137 ≈ 0.5s。要知道Full GC是会stop the world(暂停其它用户线程的运行)的,Java进程绝大部分时间都用于GC就没有时间去做其他事了。

        (6)使用jmap -histo 9180命令,查看哪些类对象占用内存较多

【诊断】linux系统下的内存溢出问题定位_第5张图片

        这里截取了占用内存资源的TOP10([C指char数组,String底层是char数组),我们发现主要是new了大部分的String对象、Integer对象存储在HashMap中,又没有即时清理HashMap中的无用数据导致内存溢出。

总结:

        1、通过top命令定位异常进程(pid)。

        2、通过top -H -p pid命令查看异常进程下,线程CPU使用率情况。

        3、通过jstack pid > log.txt命令,将线程的堆栈信息打印到log.txt文件中。

        4、通过cat log.txt | grep tid(thread id) -A 20命令,过滤出异常线程的相关信息。

        5、通过jstat -gcutil pid 2000 10命令,查看堆内存使用情况。

        6、通过jmap -histo pid命令,查看占用内存资源较多的类对象有哪些。

        基本上到了第6步你就可以知道哪些类对象导致了内存溢出,再去review代码修改bug即可。

你可能感兴趣的:(java,前端,数据库)