AWS ECS中Docker容器内Java应用因OOM重启,求内存排查方法
问题描述
我在AWS EC2(ECS)的Docker容器中运行一个部署在Tomcat服务器上的Java应用。近期该Java应用服务会不定期自动重启,查看ECS Agent日志后发现存在OutOfMemoryError。
ECS Agent日志信息:
Level: INFO Message: DockerGoClient: Process within container 02118442aba47dbe536651b90ca1cd64c43ec419ea949dbe8ca3797f0df1dd71 (name: "my-java-app10-f8e5abadedfbd0c9d901") died due to OOM Module: docker_client.go Level: INFO Message: Handling container change event Task: 3825f1c6-0d3d-4771-bed7-15332dab4ad9 Container: service-layer RuntimeID: 02118442aba47dbe536651b90ca1cd64c43ec419ea949dbe8ca3797f0df1dd71 Status: STOPPED Level: WARN Message: Error stopping the container; marking it as stopped anyway Task: 3825f1c6-0d3d-4771-bed7-15332dab4ad9 Container: service-layer RuntimeID: 02118442aba47dbe536651b90ca1cd64c43ec419ea949dbe8ca3797f0df1dd71 ErrorName: OutOfMemoryError Error: Container killed due to memory usage
我无法确定具体是哪个Java线程/进程在占用内存。已尝试以下操作:
docker inspect <containerName> docker ps --all (This shows Exited 137))
同时也查看了应用日志,但未发现异常调用。容器内存上限为1GB,此前运行正常,请问该如何排查持续占用内存的Java线程/进程?
排查方案
1. 配置JVM内存参数与OOM自动转储
修改Tomcat的JVM启动参数,添加内存溢出时的堆转储配置,保留OOM现场数据:
- 在Tomcat的
catalina.sh(Linux)或catalina.bat(Windows)中追加:
确保容器内JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof"/tmp有足够空间,或指定到挂载的持久化目录,避免转储失败。 - 结合容器1GB内存上限,调整JVM堆内存:建议设置
-Xmx768m,预留部分内存给Tomcat系统进程,避免容器直接被OOM Killer终止。
2. 实时监控容器内Java进程内存
应用运行时,进入容器排查Java进程内存状态:
- 进入容器:
docker exec -it <container-id> /bin/bash - 查找Java进程ID:
ps aux | grep java - 用
jstat监控堆内存变化:jstat -gc <pid> 1000 10(每1秒输出一次,共10次),观察堆各区域(新生代、老年代)的内存占用趋势,判断是否存在内存泄漏。 - 用
jmap查看堆内存分布:jmap -histo <pid>,列出各类的实例数量与内存占用,定位内存消耗大户。
3. 分析堆转储文件
OOM发生后,导出堆转储文件并分析:
- 复制文件到本地:
docker cp <container-id>:/tmp/heapdump.hprof ./local-directory/ - 使用Java分析工具(如Eclipse MAT、JVisualVM)打开文件:
- MAT自动生成泄漏报告,找出占用内存最大的对象及其引用链,定位到具体代码逻辑。
- JVisualVM对比不同时间点的堆快照,分析内存增长根源,同时可查看线程栈信息,排查线程内存占用。
4. 监控容器与ECS层面内存指标
- 在AWS ECS控制台查看服务的CloudWatch内存监控指标,确认内存占用是否持续上升,是否在特定时段触发OOM。
- 用
docker stats <container-id>实时监控容器整体内存、CPU使用率,对比JVM内部内存数据,排查是否是堆外内存(如直接内存、NIO ByteBuffer)占用过高。
5. 排查代码与依赖变更
- 检查近期代码改动,是否新增大量缓存、大对象创建、未关闭的资源(数据库连接、文件流)等逻辑。
- 排查第三方依赖版本,确认是否存在已知的内存泄漏Bug。
内容的提问来源于stack exchange,提问作者Raghav91
相关产品推荐
相关产品推荐

