一 发生很多Job OOM现象
那几天运维发现很多OOM,一直不断在Full GC。我们知道Full GC一旦发生超过几分钟,其他的线程均停止工作,只有垃圾回收线程工作。
第一个猜想是运行的Job,也就是我们运行任务内存资源不够用。所以猜想是container所启动的YarnChild的JVM内存大小不够,或者配置小了,导致内存不够用。我们就把内存配大了些。
mapreduce.map.memory.mb: 为每一个MapTask申请的资源,也就是每一个MapTask最多只能使用这么内存,默认1024M
mapreduce.reduce.memory.mb:为每一个ReduceTask申请的资源,一个ReduceTask最多只能使用这么内存,默认1024M
注意:设置了这个,就起作用了吗?答案是不一定的。原因在于,这里只是限制MapTask 和 ReduceTask最多使用的内存限制。
但是container所启动的YarnChild的JVM是不是就是这个配置呢,答案是不是的。启动的JVM的内存参数取决于:
mapreduce.map.java.opts:这个是Container启动MapTask的YarnChild进程设置的堆大小参数,默认1024M
mapreduce.reduce.java.opts: 这个是Container启动ReduceTask的YarnChild进程设置的堆大小参数, 默认1024M
比如:
mapreduce.reduce.memory.mb=5120 //设置reduce container的内存大小
mapreduce.reduce.java.opts=-Xms2000m -Xmx4600m; //设置reduce任务的JVM参数
如果只是把任务所允许的资源调高了,但是启动任务JVM参数并没有调高,那么还是不起作用,就相当于你可以使用1G来运行某个程序,但是JVM只能使用512M,就算你可以使用2G来运行这个程序,那么JVM还是只能使用512M.
总结:
优先设置container启动的YarnChild进程的堆参数,如果还不够,则在考虑增大mapreduce.map.memory.mb和mapreduce.reduce.
memory.mb参数,然后在来调整堆参数。
所以理想的配置应该是java.opts的值必须大于等于memory.mb的值.所以说,这种配置不当的方式也会引发频繁的Full GC.
mapreduce.map.memory.mb最好是mapreduce.reduce.memory.mb的2倍
mapreduce.map.java.opts和mapreduce.reduce.java.opts应该不会大于mapreduce.map.memory.mb和mapreduce.reduce.memory.mb
# mapreduce.map.java.opts、mapreduce.map.java.opts.max.heap最好是mapreduce.reduce.java.opts、mapreduce.reduce.java.opts.max.heap参数的0.85倍
mapreduce.map.java.opts, mapreduce.map.java.opts.max.heap=1.6G
mapreduce.reduce.java.opts,mapreduce.reduce.java.opts.max.heap=3.3G
hadoop自身已经对此进行了contaienr级别的监控,对于所有启动过container,他会额外开启一个叫container-monitor的线程,专门有对于这些container的pmem(物理内存),vmem(虚拟内存)的监控.相关的配置属于如下:
yarn.nodemanager.pmem-check-enabled:是否检查物理内存
yarn.nodemanager.vmem-check-enabled:是否检查虚拟内存
这两个参数默认是开启的。
一旦这个container所使用的内存超过JVM配置的这个内存,就会把这个container杀掉
二 Reduce在copy阶段内存占用过大
这是reduce从map取数据阶段报的错,reduce从map取数阶段使用的buffer可以占到reduce任务最大堆的70%的内存。报错之前copy还在运行,而reduce阶段其他过程占用了超过30%的内存,这个时候copy阶段继续取数,扩展buffer的时候,申请不到内存就报错了。
针对这个我们可以限制copy阶段占用buffer的大小:
mapreduce.reduce.shuffle.input.buffer.percent:默认是70%,我们可以把这个参数设置成0.5 或者更小一点。
另外我们需要注意另外一个参数,就是可以控制shuffle并行度的参数:mapreduce.reduce.shuffle.parallelcopies
因为mapreduce.reduce.shuffle.input.buffer.percent* mapreduce.reduce.
shuffle.parallelcopies< 1,否则就会报错:
Error: org.apache.hadoop.mapreduce.task.reduce.Shuffle$ShuffleError:error in shuffle in fetcher#1
二 YARN 内存资源优化配置
2.1yarn.nodemanager.resource.memory-mb
这个参数其实是设置NodeManager 预备从本机申请多少内存量的,用于所有Container 的分配及计算。这个参数相当于一个阈值,限制了NodeManager 能够使用的服务器的最大内存量,以防止NodeManager 过度消耗系统内存,导致最终服务器宕机。这个值可以根据实际服务器的配置及使用,适度调整大小。
yarn.nodemanager.resource.memory-mb=32GB
yarn.nodemanager.resource.cpu-vcores=32core
2.2yarn.nodemanager.vmem-pmem-ratio,默认是2.1
NodeManager接收到 ApplicationMaster 传递过来的Container 后,会用Container 的物理内存大小 (pmem)* yarn.nodemanager.vmem-pmem-ratio 得到 Container 的虚拟内存大小的限制,即为vmemLimit:
然后,NodeManager在monitor 线程中监控Container 的 pmem(物理内存)和 vmem(虚拟内存)的使用情况。如果当前 vmem 大于vmemLimit 的限制
Monitor每隔 3 秒钟就更新一次每个Container 的使用情况;更新的方式是:
查看/proc/pid/stat 目录下的所有文件,从中获得每个进程的所有信息;
根据当前Container 的 pid 找出其所有的子进程,并返回这个Container 为根节点,子进程为叶节点的进程树;在 Linux 系统下,这个进程树保存在ProcfsBasedProcessTree 类对象中;
然后从ProcfsBasedProcessTree 类对象中获得当前进程 (Container) 总虚拟内存量和物理内存量。
由此大家应该立马知道了,内存量是通过/proc/pid/stat 文件获得的,且获得的是该进程及其所有子进程的内存量。所以,这里的 vmem 就是 OS 层面的虚拟内存概念。
2.3yarn.scheduler.minimum-allocation-mb和yarn.scheduler.maximum-allocation-mb
yarn.scheduler.minimum-allocation-mb:单个container可以申请到的最小内存量
yarn.scheduler.maximum-allocation-mb:单个container可以申请到的最大内存量
#我们可以根据这个yarn.scheduler.minimum-allocation-mb参数计算一个节点最大container数量:yarn.nodemanager.resource.memory-mb
/单个 container可以申请到最小内存量
# yarn.scheduler.maximum-allocation-mb不能大于yarn.nodemanager.
resource.memory-mb:即每一个container可以申请最大内存量不能超过分配给NodeManager的内存量