最近发现Oozie上有个任务经常会跑失败,oozie的日志(标准输出、标准错误输出、系统日志)都看上去很正常,没有异常输出.
继续查看nodemanager上的容器日志
在容器的系统日志中看到以下错误,得知是物理内存超过了限制。
分析了一下代码,不应该占用这么多内存,在本地跑了一下这个任务,发现内存一直维持850M左右,而我的容器内存给了2G。仔细研究了一下Yarn的内存参数,Map任务需要预留一些内存给除JVM以外开销。这里有几个重要参数
这三个参数其实是相互关联的,通过任意两个可以计算剩下的一个,没明白为什么要给三个参数而不是2个,报错时mapreduce.map.java.opts.max.heap给的是1.6G,JVM的内存给的太高,导致容器整体物理内存超了,将这个参数调整到
1.4G 。按照https://issues.apache.org/jira/browse/MAPREDUCE-5785这个issue的说法mapreduce.job.heap.memory-mb.ratio 参数在3.0.0-alpha1 版本前是不起作用的。
本以为问题就此解决,但过了一段时间问题又出现了,偶然发现这条命令并没有用到之前设置的虚拟机参数,仔细一想这个任务本质是调用一个Shell脚本,java进程是在脚本中启起来的,而我在脚本中没有设置-Xmx参数默认使用系统内存的1/4,大约16G。因此当JVM内存占用增加导致容器内存超过2G时就会导致上面的错误。
问题到这里虽然已经解决了,但还是有一个疑点,虚拟内存实际使用19.4G 已经远远超过了期望的 4.2G .为什么虚拟内存没有超过限制呢 ?
根据https://issues.apache.org/jira/browse/YARN-4714的描述这是一个Bug,容器不应该因为虚拟内存超过限制而被杀掉,并且默认的容器虚拟-物理内存比为2.1,这实在太低了。
检查参数,发现在我们使用的CDH 5.9.0版本中虚拟内存检查已经默认关闭了,所以即使虚拟内存超了,容器也不会被杀死。
至于虚拟内存使用过多有没有什么危害,其实用个10多G,问题不大,具体可以参考https://www.cnblogs.com/seasonsluo/p/java_virt.html