您好,登录后才能下订单哦!
今天就跟大家聊聊有关如何理解Java进程的OOMKiller,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
登上机器后,查看应用和中间件日志,确实没有看到问题。我怀疑是JVM OOM了但没有配置输出,正想加上OOM时的堆栈输出参数,但发现应用启动命令已经包含类似的参数:-XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=./hs_err_pid<pid>.log -XX:HeapDumpPath=./java_pid<pid>.hprof
。 看来从应用层面查不到什么了,那就再看看系统日志吧。 使用dmesg -T | grep java
查看系统日志 果然,从dmesg
倒数第二行返回的信息来看,确实是由于oom导致的,但是这里的oom并非JVM的oom,而是Linux系统的oom。 >Killed process xxx(java), total-vm:7539096kB, anon-rss:3919048kB file-rss:17312kB, shmem-rss:0kB。
在这台物理内存为4GB的机器上, `total-vm`是已经分配给java进程的虚拟内存数量(7539096kb=7.02GB) `anon-rss`是java进程物理内存使用量(3919048kB=3.65GB) `file-rss`是java进程映射到设备或文件系统的使用量(17312kB=16.51MB)
网上已经有很多介绍Linux oom killer的文章了,这里只简单概括:Linux系统在分配物理内存时,如果内存不足(什么时候不足?)oom killer会选择oom score得分最高的进程杀掉以释放内存。
看起来问题很明确了,是Linux的oom killer杀掉了Java进程。但是我还是有个疑问,同样是OOM,为什么没有触发JVM的OOM呢?带着这个疑问,继续了解了Linux的内存分配机制。
以下简单说一下我的理解,真实分配机制要复杂的多。有不对的地方请大家指正。 Linux有一套虚拟内存机制,当进程向系统申请内存时,总体上系统可以有两种方式答复进程: a.检查是否真的还有足够的内存(实际值计算一般等于Swap+RAM*overcmmit_ratio)满足需求,如果有就满足分配,如果没有就以分配失败答复集成; b.不检查,直接分配。
这里的分配
指的都是进程的虚拟内存地址的增长。这里的b叫做"Overcommit",也就是发生了超过系统可接受内存范围的分配。这里的b其实是基于一种乐观的估计,因为申请的内存不一定用到。但是一旦进程需要使用所能提供的实际内存时,就会导致OOM,此时oom killer就会通过排序oom score牺牲掉一个或多个得分最高的进程,以此来释放内存。
再强调一次理解Overcommit的关键点,内存申请不等于内存分配,内存使用时才真正分配。
那么JVM的OOM呢?
>One common indication of a memory leak is the java.lang.OutOfMemoryError
exception. Usually, this error is thrown when there is insufficient space to allocate an object in the Java heap. In this case, The garbage collector cannot make space available to accommodate a new object, and the heap cannot be expanded further. Also, this error may be thrown when there is insufficient native memory to support the loading of a Java class. In a rare instance, a java.lang.OutOfMemoryError
may be thrown when an excessive amount of time is being spent doing garbage collection and little memory is being freed. 摘自Oracle内存泄露说明相关文章
简单来说,发生JVM OOM的原因有: Java堆内存不足,垃圾回收器不能给新对象腾地儿,堆也无法扩展(Java heap space); 本地内存不足以支持加载Java类(Metaspace or Perm); 不常见地,过长时间的垃圾回收没有释放多少内存也会导致OOM(GC Overhead limit exceeded)。 回头看看,其实是Linux系统把Java进程给“骗”了。当开启了允许overcommit的策略时,如果Java进程或其他任何进程申请了可能过多(超过系统能提供的)虚拟内存时,只要系统内存还足够使用,Java进程并不会发生OOM。而当Linux系统发现分配内存真的不够时,就会把oom score最靠前的Java进程悄无声息地干掉(kill -9
)。
>Java老农:“爷,给俺批点地,俺要种瓜。” Linux老爷:“爷我高兴,给你4096亩地,随便耍!” Java老农种地中...1...2...4....1024... Java老农:“没有虫子骚扰太开心啦,我要这样到永...” Java卒 Linux老爷:“有没有人要地呀,老爷我有的是地啊!”
分析完原因,其实有很多解决方案。
如果有资源,就先尝试最简单的办法吧。
现在的机器一般都是容器或虚拟机上只要只跑一个应用,如果发生OOM,最根本的解决之道还是要回归到代码上。
让系统在申请时就报错,保守也保险。及早暴露问题,及早进行修复。同时可以修改overcommit_ratio的值改变CommitLimit。 配置参见附录。
对于一些多应用进程混用的机器,可以保护关键进程不被kill掉。 sudo echo -1000 > /proc/$pid/oom_score_adj
修改panic_on_oom
参数可以关闭OOM Killer。玩玩还可以,线上系统不建议用,否则系统死给你看。
可在/proc/sys/vm/overcommit_memory
配置或查看。
>0:默认值。启发式策略,比较严重的Overcommit将不能满足,而轻微的Overcommit将被允许。另外,root能Overcommit的值比普通用户要稍微多些,一般为3%。 1:允许Overcommit,这种策略适合那些不能承受内存分配失败的应用,比如某些科学计算应用。(本文中涉及的系统就是开启的这个策略) 2:禁止Overcommit,在这个情况下,系统所能分配的内存不会超过下面的CommitLimit
,计算方法为Swap + RAM * /proc/sys/vm/overcmmit_ratio
,默认50%,可调整),如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序。
grep -i commit /proc/meminfo CommitLimit: 6201492 kB # 虚拟内存限定值 Committed_AS: 5770836 kB # 已分配内存,如果大于CommitLimit说明开启了允许Overcommit的策略
# 进程oom得分,0不kill cat /proc/{pid}/oom_score # 进程oom调整兼容,计算时一般会以oom_score_adj替换 cat /proc/{pid}/oom_adj # 用户打分调整。最小值-1000,将会禁止oom killer杀此进程。 #取值从-1000到1000,表示对最终得分的折扣到惩罚。 cat /proc/{pid}/oom_score_adj
看完上述内容,你们对如何理解Java进程的OOMKiller有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。