为什么docker java应用程序会因为内存而被杀死?
问题:由于内存使用,应用程序被终止为什么docker java应用程序会因为内存而被杀死?,java,amazon-web-services,docker,memory,amazon-ecs,Java,Amazon Web Services,Docker,Memory,Amazon Ecs,问题:由于内存使用,应用程序被终止 Status reason OutOfMemoryError: Container killed due to memory usage Exit Code 137 环境:AWS ECS实例上docker容器中的Spring Boot应用程序,配置: AWS硬盘内存限制/总RAM-384 MB -Xmx134m -Xms134m -XX:MaxMetaspaceSize=110m 根据java max内存公式(我在几周的研究中发现了这个公式,并且
Status reason OutOfMemoryError: Container killed due to memory usage
Exit Code 137
环境:AWS ECS实例上docker容器中的Spring Boot应用程序,配置:
- AWS硬盘内存限制/总RAM-384 MB
- -Xmx134m
- -Xms134m
- -XX:MaxMetaspaceSize=110m
max_memory=134(xmx)+110(元空间)+40(非堆-非元空间)+9(线程)*1(默认Xss)=293
但是,在加载heapUsedMemory=~105-120mb和非heapUsedMemory(metaspace+JVM stuff)=~140mb的情况下,这意味着必须有384-120-140=124MB的可用内存。
所以问题是有大量的可用内存,所有java工具都在展示(jstat-gc、grafana上的Spring、不同的JavaAPI等等)。
以下是我在研究过程中开发和使用的API的代码片段:
@GetMapping("/memory/info")
public Map<String, String> getMmUsage() {
Map<String, Long> info = new HashMap<>();
List<MemoryPoolMXBean> memPool = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean p : memPool) {
if ("Metaspace".equals(p.getName())) {
info.put("metaspaceMax", p.getUsage().getMax());
info.put("metaspaceCommitted", p.getUsage().getCommitted());
info.put("metaspaceUsed", p.getUsage().getUsed());
}
}
info.put("heapMax", ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax());
info.put("heapCommitted", ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getCommitted());
info.put("heapUsed", ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed());
info.put("non-heapMax", ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getMax());
info.put("non-heapCommitted", ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getCommitted());
info.put("non-heapUsed", ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed());
Map<String, String> memoryData = info.entrySet().stream().collect(Collectors.toMap(Entry::getKey, e -> {
long kb = e.getValue() / 1024;
return (kb / 1024) + " Mb (" + kb + " Kb)";
}, (v1, v2) -> v1, TreeMap::new));
Set<Thread> threads = Thread.getAllStackTraces().keySet();
memoryData.put("threadsCount", Integer.toString(threads.size()));
memoryData.put("threadsCountRunning",
Long.toString(threads.stream().filter(t -> t.getState() == Thread.State.RUNNABLE).count()));
return memoryData;
}
@GetMapping(“/memory/info”)
公共映射getMmUsage(){
Map info=newhashmap();
List memPool=ManagementFactory.getMemoryPoolMXBeans();
for(MemoryPoolMXBean p:memPool){
if(“Metaspace”.equals(p.getName())){
info.put(“metaspaceMax”,p.getUsage().getMax());
info.put(“metaspaceCommitted”,p.getUsage().getCommitted());
info.put(“metaspaceUsed”,p.getUsage().getUsed());
}
}
info.put(“heapMax”,ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax());
info.put(“heapCommitted”,ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getCommitted());
info.put(“heapUsed”,ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed());
info.put(“non-heapMax”,ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getMax());
info.put(“non-heapCommitted”,ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getCommitted());
info.put(“非heapUsed”,ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed());
Map memoryData=info.entrySet().stream().collect(Collectors.toMap)(条目::getKey,e->{
长kb=e.getValue()/1024;
返回值(kb/1024)+“Mb”(+kb+“kb”);
},(v1,v2)->v1,TreeMap::new);
Set threads=Thread.getAllStackTraces().keySet();
memoryData.put(“threadscont”,Integer.toString(threads.size());
memoryData.put(“threadscontrunning”,
Long.toString(threads.stream().filter(t->t.getState()==Thread.State.RUNNABLE.count());
返回内存数据;
}
因此,我的应用程序应该是稳定的,因为它有很多内存要处理。但事实并非如此。如上所述,由于内存使用,容器被杀死
正如我上面所描述的,java工具显示有大量内存,并且内存(堆)正在被释放。另一方面,AWS cloudwatch度量内存利用率显示内存不断增长(非常小的部分):
有趣的探索:在无休止的测试中,我发现了下一个问题:当我设置xmx=134MB时,应用程序的寿命更长,能够经受5轮性能测试,当我设置xmx/xms=200mb时,它经受住了1轮性能测试。这怎么可能呢
我的观点:似乎有东西在使用内存,但没有正确释放内存
我很想听听您的意见,为什么我的应用程序在有50+mb的可用内存时会不断消亡,为什么AWS指标显示出与java工具不同的结果java应用程序也可以分配本机内存,而本机内存不是任何堆、堆栈空间的一部分,例如
ByteBuffer.allocateDirect
或从Java使用的本机库、Java本身的内存等@markrottevel我知道这一点。但我们处理的不是缓冲区,也不是文件。对于任何本机库来说,100MB似乎也足够处理etc@NyamiouTheGaleanthropeGC配置是默认的(规范:openjdk 8,debian OS)您是否能够理解我遇到的这个问题nodejs@Liran不完全是,我们发现,自从最新的Java8发行版或Java9+以来,docker中就有了自动处理内存的标志,从那时起,它变得更好了