EDAS——如何快速定位OOM问题

    xiaoxiao2025-10-28  2

    本期分享专家:滨雨,有多年软件开发 + 项目管理经验,先后在华为、阿里基础架构部任职,在阿里云主要从事存储、cdn、视频、中间件等技术领域的支持,喜欢新技术,喜欢钻研,喜欢各种新的挑战。

    什么是OOM?

    相信很多“程序猿”都能知道,OOM 异常,就是我们常见的: “java.lang.OutOfMemoryError” 在应用开发中,是比较常见的一种异常,主要分为三种: 1. OutOfMemoryError: PermGen space 2. OutOfMemoryError: Java heap space 3. OutOfMemoryError:unable to create new native thread

    OOM的危害

    出现OOM的情况,往往会导致: 1、应用服务异常 2、线程异常 3、程序崩溃 以及其他未知的问题,相信看到这几点就能体会到 “ OOM ” 的“ 恐怖 ”了。

    如何排查OOM问题

    在遇到这OOM问题的时候,要如何分析原因,是直接去死磕代码?还是去调整工程架构?我相信这样的方式很多时候都只会徒劳,那针对这类问题,我们可以如何快速的去排查定位呢?

    模拟异常场景

    为了说清楚整个排查过程,我们编写一段OutOfMemoryError测试代码,用来模拟出 oom 场景。 servlet 测试代码:

    @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("heap test begin"); List<TestCase> cases = new ArrayList<TestCase>(); while(true){ cases.add(new TestCase()); } //super.doGet(req, resp); } public class TestCase { public int id; public String name; public String[] array = new String[1024]; public List<String> list = new ArrayList<>(1024);

    web.xml映射路径:

    <servlet> <servlet-name>Heaptest</servlet-name> <servlet-class>com.alibaba.edas.tradeshop.threadtest.HeapOutOfMemory</servlet-class> </servlet> <servlet-mapping> <servlet-name>Heaptest</servlet-name> <url-pattern>/heap.htm</url-pattern> </servlet-mapping>

    获取dump文件

    Dump文件是进程的内存镜像,可以把程序的执行状态通过调试器保存到dump文件中,是开发人员定位jvm问题的“利器”。

    方式一:edas控制台部署添加参数

    通过edas控制台部署启动应用,可以指定jvm参数,在控制台上指定最大Heap Size 和初始化Heap Size 都为100m  (注意现在配置的值只是为了测试), 另外还可以自定义加上一些参数,例如:

    -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError XX:HeapDumpPath=/home/admin/dump/

    控制台上设置:

    PrintGCDetails 会将gc日志打印出来,XX:HeapDumpPath指定dump文件存储路径,当出现OOM问题时,会在/home/admin/dump/生成一个dump文件

    方式二:通过jmap获取

    jmap 是 jdk自带的一个 jvm 检测工具,为了能让通过jmap方式的时候获取到堆栈溢出的dump文件,我们将代码改为:

    @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("heap test begin"); List<TestCase> cases = new ArrayList<TestCase>(); while(true){ try{ cases.add(new TestCase()); }catch(Throwable t){ } } //super.doGet(req, resp); }

    访问到测试代码入口,在应用服务器上执行:

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

    可以获取到heap.bin文件。

    测试堆栈溢出

    admin用户登录到服务器上,找到tomcat进程ID,执行 ./jstat -gcutil {pid} {时间间隔} ,观察 gc 执行情况:

    可以看到当前YGC 和 FGC 都不频繁,属于正常状态。

    当我们访问测试路径: http://{ip}:port/path/heap.htm

    执行结束后,可以发现,eden 区和 old 区 都瞬间打满,而且短时间内发生多次 FGC:

    MAT 内存分析

    mat 是一个快速分析 java 内存的工具,mat 官网 到服务器上,之前我们指定的路径下面,方式一:/home/admin/dump/ 可以找到生成的 hprof 文件,方式二:生成的heap.bin文件,下载下来后,用 mat 内存分析软件打开

    经过mat 分析后,马上可以得出一份分析报告,从这可以很清晰的定位到,是由于 “ HeapOutOfMemory.doGet ” 这段代码入口导致的堆栈溢出,终于,我们的“罪魁祸首”付出水面了,原来,是由于代码中出现了一段死循环一直在新建class 类,导致堆栈溢出,接下来,该知道怎么去修改代码了吧。

    小结

    当遇到jvm问题的时候,不必惊慌,也不要因为是技术底层问题而感到无从下手,我们有很多种方法可以方便定位到原因,除了本文中提到的 mat 内存分析工具,我们还有很多其他的小工具可以利用起来,jdk 本身就提供了很多这样的支持工具,例如: jconsole、jmap、jstat、jinfo、jvisualvm 等等,会利用好这些小工具,下次再遇到类似的问题后,就可以得心应手了。

    关注
    0人关注该文章
    评论文章 (0)
    最新回复(0)