记生产OOM的故障分析

一、引言

生产上告警,交易堵塞,服务无响应,使用jstack、jmap、jhat命令进行故障分析。

Java虚拟机(Java Virtual Machine,简称JVM)作为Java语言的核心组件,为Java程序提供了运行环境和内存管理机制。本文将系统地介绍JVM的基本架构、工作原理以及相关实战案例,旨在帮助读者对JVM有更全面且深入的理解。

二、JVM概述

JVM是Java平台的一部分,负责将Java字节码转换为机器指令并在不同的操作系统上执行。它屏蔽了底层硬件和操作系统的差异,使得“一次编写,到处运行”的理念得以实现。

三、JVM架构

四、垃圾回收

使用jstat 查看内存变化情况以及垃圾回收次数、时间,可以看到Eden的内存满了之后,就会做一次YGC,老年代的内存满了之后,会做FGC。

jstat -gcutil 1992 1000 1000

代码如下:

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: thinkpad
 * @Date: 2024-02-04 22:19
 */
public class GCDemo {

    public static void main(String[] args) throws InterruptedException {

        Thread.sleep(10000);

        Map gc1Map = new HashMap();
        Map gc2Map = new HashMap();
        for(int i = 0; i < 10000; i++){
            // 每个byte占1字节,所以为了得到1M需要大约1024 * 1024个字节
            int oneMegabyte = 1024 * 1024;

            // 创建一个1MB的byte数组
            byte[] largeObject = new byte[oneMegabyte];
            gc1Map.put(i, largeObject);
            gc2Map.put(i, largeObject);
            System.out.println("i = " + i);
            Thread.sleep(200);
        }
    }
}

五、实战案例

本次生产上的故障是由于使用POI进行Excel操作所引起的,现在,我们来分析POI导致的原因。

生产上的故障,使用XSSFWorkbook操作十几万条记录,代码大致如下:

import org.apache.commons.compress.utils.Lists;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.util.List;

/**
 * @Author: thinkpad
 * @Date: 2024-02-24 21:53
 */
public class XssfWorkbookDemo {

    public static void main(String[] args) {
        try {
            File testcaseFile = new File("E:/testcase.xlsx");
            XSSFWorkbook xssfWorkbook = new XSSFWorkbook(testcaseFile);
            XSSFSheet sheet = xssfWorkbook.getSheetAt(0);

            List<TestCase> testCaseList = Lists.newArrayList();
            int lastRowNum = sheet.getLastRowNum();
            System.out.println("lastRowNum: " + lastRowNum);
            for(int rowIndex = 1; rowIndex <= lastRowNum; rowIndex++){
                XSSFRow row = sheet.getRow(rowIndex);
                String[] cellArray = new String[row.getLastCellNum() - row.getFirstCellNum()];
                for(int cellNum = row.getFirstCellNum(); cellNum < row.getLastCellNum(); cellNum++){
                    cellArray[cellNum] = row.getCell(cellNum).toString();
                }

                TestCase testCase = new TestCase();
                testCase.setCaseId(cellArray[0]);
                testCase.setUrl(cellArray[1]);
                testCase.setLevel(cellArray[2]);
                testCase.setDescribe(cellArray[3]);
                testCaseList.add(testCase);
            }

            testCaseList.stream().forEach(testcase->{
                System.out.println(testcase.toString());
            });
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

导致内存溢出,通过jmap和jhat分析,使用jmap导出内存,命令如下

jmap -dump:live,format=b,file=heap-dump-pid.bin pid

通过jhat分析,关注 show heap histogram(内存实例的分布)

通过show heap histogram分析,发现内存占用多的如下分布, org.apache.xmlbeans.impl.store.Xobj占用了大量的内存,这是因为使用POI读取excel时,会产生大量的xml解析,因此如果有很多记录时,就会导致内存溢出。

 由于使用XSSFWorkbook读取大量excel记录时,会内存溢出,因此尝试SXSSFWorkbook读取excel,代码大致如下

package com.fd.demo;

import org.apache.commons.compress.utils.Lists;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.util.List;

/**
 * @Author: thinkpad
 * @Date: 2024-02-24 21:53
 */
public class SXssfWorkbookDemo {

    public static void main(String[] args) {
        try {
            File testcaseFile = new File("E:/testcase.xlsx");
            FileInputStream fis = new FileInputStream(testcaseFile);
            XSSFWorkbook xssfWorkbook = new XSSFWorkbook(fis);
            SXSSFWorkbook workbook = new SXSSFWorkbook(xssfWorkbook, 1000);
            Sheet sheet = workbook.getXSSFWorkbook().getSheetAt(0);


            List<TestCase> testCaseList = Lists.newArrayList();
            int lastRowNum = sheet.getLastRowNum();
            System.out.println("lastRowNum: " + lastRowNum);
            for(int rowIndex = 1; rowIndex <= lastRowNum; rowIndex++){
                Row row = sheet.getRow(rowIndex);
                String[] cellArray = new String[row.getLastCellNum() - row.getFirstCellNum()];
                for(int cellNum = row.getFirstCellNum(); cellNum < row.getLastCellNum(); cellNum++){
                    cellArray[cellNum] = row.getCell(cellNum).toString();
                }

                TestCase testCase = new TestCase();
                testCase.setCaseId(cellArray[0]);
                testCase.setUrl(cellArray[1]);
                testCase.setLevel(cellArray[2]);
                testCase.setDescribe(cellArray[3]);
                testCase.setDescribe1(cellArray[4]);
                testCase.setDescribe2(cellArray[5]);
                testCase.setDescribe3(cellArray[6]);
                testCase.setDescribe4(cellArray[7]);
                testCase.setDescribe5(cellArray[8]);
                testCase.setDescribe6(cellArray[9]);
                testCase.setDescribe7(cellArray[10]);
                testCase.setDescribe8(cellArray[11]);
                testCase.setDescribe9(cellArray[12]);
                testCase.setDescribe10(cellArray[13]);
                testCase.setDescribe11(cellArray[14]);
                testCase.setDescribe12(cellArray[15]);
                testCase.setDescribe13(cellArray[16]);
                testCase.setDescribe14(cellArray[17]);
                testCase.setDescribe15(cellArray[18]);
                testCase.setDescribe16(cellArray[19]);
                testCase.setDescribe17(cellArray[20]);
                testCase.setDescribe18(cellArray[21]);
                testCaseList.add(testCase);
            }
            for(int i = 0; i < testCaseList.size(); i++){
                System.out.println(testCaseList.get(i).toString());
            }
            Thread.sleep(1000000);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 导致内存溢出,通过jmap和jhat分析,使用jmap导出内存,命令如下

jmap -dump:live,format=b,file=heap-dump-pid.bin pid

通过jhat分析,关注 show heap histogram(内存实例的分布)

六、总结

内存溢出的分析需要用到jmap,jhat命令

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值