Nodemanager堆内内存泄漏问题记录

来源:互联网 发布:电商系统源码 编辑:程序博客网 时间:2024/05/17 09:06

一.问题发现:

发现nm堆内存使用率过高:


<图一.1>

这里面可以看到老年代有大量对象未被回收。


<图一.2>

这里面有很多的这种对象,后面会一一指出他们为什么这么多。

而且横向比较这些nm的内存信息,会发现nm的内存的占用多少都是因为这些对象是线性增长的。

二.问题分析:

这些对象是在哪儿创建的呢?为什么不会被回收呢?

好的,先让我们放下这个问题,先来了解一下nm里面的localizing机制。

所谓localizing就是在启动container之前由nm负责做一些资源下载的操作。更具体一点就是container的Private类的资源下载:

比如:


<图二.1>

Nm上的这两个目录。

详细的流程就是这样:


<图二.2>

然后,我们针对最关键的LocalDirAllocator#getLocalPathForWrite方法延伸进去讲:


<图二.3>

首先需要知道,LocalDirsHandlerService是一个服务:

(Nm里多有的模块都是基于对象服务化构建起来的,

更全面的服务层次后面再介绍)


<图二.4>

在这里需要留意一下图二.3中getLocalPathForWrite方法的“getConfig()”参数。

继续往下看,在方法体中:


会先拿到context对象,这个对象负责维护全局唯一的

文件目录情况概览。为什么这么说,因为进入到代码

你会看到context是全局唯一的:


从注释可以看出,context这个map根据上下文名称存储

对应的AllocatorPerContext上下文对象,举个例子“

比如类似mapred.local.dir,或者yarn.nodemanager.local-dirs”

等。

所有nm中需要yarn.nodemanager.local-dirs这个上下文的

服务都会使用相同的AllocatorPerContext对象,比如上面介绍的

LocalDirsHandlerService服务,这一点很重要。

接着调用,context的getLocalPathForWrite(pathStr, size, conf, checkWrite)方法。


在这个方法里面只做两件事:

第一:检查当前的目录情况,

/data0/hadoop/local,/data1/hadoop/local,/data2/hadoop/local,/data3/hadoop/local,/data4/hadoop/local,/data5/hadoop/local,/data6/hadoop/local,/data7/hadoop/local,/data8/hadoop/local,/data10/hadoop/local,/data11/hadoop/local

在confchange方法里面检查的方式就是简单检查变量——savedLocalDirs

和新传进来的的conf对象里面的配置是否一致:


如果不一致,会进到if里面,重新获取本地目录。

可以看到其中有一行代码是获取本地文件系统的:

localFS = FileSystem.getLocal(conf)

那么文章最开始打出的堆栈中那么多的FileSystem对象是不是这里创建的呢?

带着这个疑问进到FileSystem.getLocal(conf)方法里看:

首先:

会构建一个默认的URI去获取FS对象:


这个LocalFileSystem.NAME即是一个“file:///”默认的本地URI

层层跟进代码会发现会创建很多个FS存在map里,即FS的一个

缓存机制,这其中会创建很多的URI,Subject,Key,UGI对象,并且

由于这些对象都是存在map里面的,所以永远不会被回收。

所以,可以确定就是这里引起了Nm内存的泄漏。

此处代码太多,就贴上来了,有兴趣的同学可以自行查看。

根本原因清楚了,现在我们回到最初的confChange方法上,

是什么原因引起了conf配置不一样呢,按理说Nm里面的每个服务都是对应的同一个

Configuration对象啊?为什么会有不一样的?

在解释这个问题之前,先看一下服务对象的层次结构:


所有服务都是由上到下递归的调用serviceInit(Configation)方法,并把Configation参数设到对象内部,所以

可以说每个服务使用的conf都是同一个对象。但是ShuffleHandler不一样,

ShuffleHandler的serviceInit方法中会新创建一个Configation:

super.serviceInit(new Configuration(conf));

在了解了这个情况之后,我们再来看ShuffleHandler会做什么事情:

其实这个类就是负责NIO数据读写的,在内部类Shuffle中

populateHeaders方法:


会去调用LocalDirAllocator类的读方法,前面说过此方法实质会调用

AllocatorPerContext对象的getLocalPathToRead读方法,而且
AllocatorPerContext是全局唯一的。所以如果conf参数不一样,会创建FileSystem
对象.

好了,基本上这个问题已经很清楚了,最后还有一点,两个conf都是出自同源,只不过在ShuffleHandler

里面把他克隆了一份,那么这两个conf(LocalDirsHandlerService和ShuffleHandler)其中哪一个会改变呢?

答案是在LocalDirsHandlerService类中会有一个自检线程,周期的扫描本地磁盘,根据磁盘情况重新组成

yarn.nodemanager.local-dirs参数值并设置到自己的conf中,但是ShuffleHandler的conf由于是新的对象

所以得不到更新。

三.解决方法:








0 0