记一次OutOfMemory定位过程

背景

最近有个项目部署到了AWS,部署方案是ECS(Launch type=FARGATE, 4CPU+4G)+Docker+Java,运行后发现程序表现不符合预期——每当任务繁忙时大量的task会被关闭并启动新的task,关闭原因都是OutOfMemory,甚至连2个线程的并发能力都没有。

Details
---
Status reason | OutOfMemoryError: Container killed due to memory usage
Exit Code | 137

Timeline

找了几个典型的case,首先在AWS上轻而易举地复现此问题,然后把数据移植到本地测试。但从jvisualvm中观察JVM heap size一直十分平稳,没有出现OutOfMemory。由于应用主要承担计算任务并有大量的IO操作,故花了几天时间研究怎么减少IO读写,却一无所获,直到昨天意外发现有段代码输出不符合预期

private static final int MB_UNIT = 1024 * 1024;
public void scheduleTask() {
    try {
        long freeMemory = Runtime.getRuntime().freeMemory();
        LOGGER.info("start batchCalculation usedMemory={}MB freeMemory={}MB", (Runtime.getRuntime().totalMemory() - freeMemory) / MB_UNIT, freeMemory / MB_UNIT);
        
        ...
        
        freeMemory = Runtime.getRuntime().freeMemory();
        LOGGER.info("finish batchCalculation usedMemory={}MB maxMemory={}MB freeMemory={}MB", (Runtime.getRuntime().totalMemory() - freeMemory) / MB_UNIT, Runtime.getRuntime().maxMemory() / MB_UNIT, freeMemory / MB_UNIT);
    } finally {
        MDC.clear();
    }
}

在AWS跑出的结果

2018-05-30 09:45:00,000 INFO class=c.m.schedule.ScheduledTasks thread=scheduled-task-pool-1 request_id="24da9c0c-e3e5-451f-8b5d-0898c68252cc" service_name=api event_description="start batchCalculation usedMemory=905MB freeMemory=1982MB"
2018-05-30 09:45:10,016 INFO class=c.m.schedule.ScheduledTasks thread=scheduled-task-pool-1 request_id="24da9c0c-e3e5-451f-8b5d-0898c68252cc" service_name=api event_description="finish batchCalculation usedMemory=905MB maxMemory=6651MB freeMemory=1982MB"

其中maxMemory=6651MB明显超过4G。应用使用的JVM参数如下:

-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1

若上述参数生效,JVM的heap size为容器最大的可用内存(即~4G)。于是猜测问题并不是出在应用,而是ECS和JVM配置,JVM在扩容时以机器的可用内存(6G)为上限,然而ECS已设置容器内存上限为4G,当任务繁忙时,应用尝试申请超过4G的内存,触发了容器的内存上限条件导致被关闭。为了验证猜想,推送了一个新的image到ECS并运行

ENTRYPOINT exec java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm -version

得到结果如下:

VM settings:
Max. Heap Size (Estimated): 6.50G
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (build 1.8.0_151-8u151-b12-1~deb9u1-b12)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)

从上面的结果可知JDK版本并没有问题而只是参数没有生效,于是暂时使用Xmx/Xms参数限制JVM heap size,修改启动命令并重新推送image和部署

ENTRYPOINT exec java -Xmx3072m -Xms3072m -XshowSettings:vm app.jar

启动后看到VM设置:

VM settings:
Min. Heap Size: 3.00G
Max. Heap Size: 3.00G
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

开启4个线程并发运行20分钟后一切如常,没有OutOfMemory,故可以确定猜测是正确的,而为什么参数不生效,原因待查。

参考资料

猜你喜欢

转载自www.cnblogs.com/hiver/p/9117760.html