记一次kettle6升级kettle8引发的内存泄漏

起因

基于一些原因,需要将kettle6.1升级到新版的kettle8.2。升级后发现kettle8每隔几天出现

GC overhead limit exceeded 或者 OutOfMemoryError:Java heap space;

一开始猜测kettle8为了提高性能,而牺牲更多的堆内存,便从开始的Xmx2048m 增加到 Xmx4096m.

可是没过几天还是一样出现问题,又从4096m增加到6144m.

问题依旧得不到解决.

 

通过排查日志发现内存溢出的问题发现: 定时重复执行的作业出现数据库连接不上,

导致好几天的作业一直都在报错数据库连接拒绝,一直到内存溢出,kettle奔溃.

排查发现,只要作业是正常执行,kettle8.2就不会出现内存溢出的问题.

但是只要有作业一直报异常,那么只要1-2天(也可能更短,看作业执行频率),

kettle8.2就会内存溢出挂掉,

kettle6.1不会出现此种情况.

 

场景重现

测试环境部署kettle8.2,模拟重现.

在kettle8.2 启动脚本增加 

-Dcom.sun.management.jmxremote.port=9008 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

本地jconsole可以远程实时监控kettle运行时的内存与线程情况

增加

-Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n

本地idea可以远程debug

 

在idea中git clone kettle8.2的源码.

 

往kettle上发了10个作业,每隔5秒重复执行.其中9个作业是正常执行的,

1个作业连接一个不存在的数据库,会一直报连接拒绝错误

jconsole监控如下图


记一次kettle6升级kettle8引发的内存泄漏_第1张图片
 

红框时间区间是该错误job停用的时间段,期间内存能维持稳定。
在测试服务器上使用命令jmap -heap,发现老年代内存居高不下,无法回收。

 

排查

 

再使用命令jmap -histo:live >> a.log(JVM会先触发gc)排查哪些对象没有被回收

 

           327          83712  org.pentaho.di.trans.Trans

           327          20928  org.pentaho.di.trans.steps.selectvalues.SelectValuesMeta

           327          20928  org.pentaho.di.trans.steps.tableinput.TableInputMeta

           327          31392  org.pentaho.di.trans.steps.insertupdate.InsertUpdateMeta

           866          27712  org.pentaho.di.trans.TransHopMeta

 

基本上可以断定是每次错误执行的作业中的转换(trans)没有被GC回收。

但是仅根据堆内存中的对象数量无法判断是哪里还在引用这些对象。

 

使用命令 jmap -dump:live,format=b,file=./dump.dat 生成内存镜像

使用命令 jhat -J-Xmx1024M ./dump.dat (jdk自带分析工具)

执行后等待console 中输入start HTTP server on port 7000 即可使用浏览器访问 IP:7000

 


记一次kettle6升级kettle8引发的内存泄漏_第2张图片
随机查看了几个引用trans对象的对象,看不出什么

kettle的对象互相引用太多太复杂,jhat这个工具很难直观找到最上层的引用对象

 

下载dump文件到本机,使用eclipse的插件 memory anaylize 分析dump

如下图


记一次kettle6升级kettle8引发的内存泄漏_第3张图片

在树形图上容易trans对象没有被回收是因为被最上层的HashMap引用

 

在树形图中找到容易定位问题代码对象是

org.pentaho.osgi.blueprint.collection.utils.ServiceMap

查看源码,猜测作业执行异常的情况,没有执行itemRemoved方法,导致本该被回收的对象在内存越积越多

在源码中新增、修改两个方法打上断点,开启远程debug 如下图
记一次kettle6升级kettle8引发的内存泄漏_第4张图片

 

调试一个正常的作业和错误的作业,发现确实如推测的,执行报错的作业最终没有执行itemRemoved.

调试过程跳过

定位具体代码 org.pentaho.di.trans.Trans

 

  public void execute( String[] arguments ) throws KettleException {
    prepareExecution( arguments );
    startThreads();
  }

 

 正常执行的job 会在 prepareExecution方法中的transMeta.setEmbeddedMetastoreProviderKey(....)

中调用serviceMap的itemAdded()

并在startThreads()方法中的transMeta.disposeEmbeddedMetastoreProvider()中

调用serviceMap的itemRemoved()

 

异常的job会在prepareExecution方法中初始化step时抛出数据库连接异常,导致后续不会进入到startThreads().

 

这下内存溢出的问题明确了,稍微修改源码,打包替换原先的kettle-engine.8.2.0.jar.

至此,kettle8.2内存泄漏问题彻底解决

后续

1、对比kettle6和kettle8的源码,发现了 EmbeddedMetastoreProvider   
   是kettle8新增的一个点,所以kettle6此场景的内存泄漏
2、测试过kettle8.3,也存在与8.2一样的内存泄漏问题.

 

 

 

 

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐
  • —软件人才免语言低担保 赴美带薪读研!—



你可能感兴趣的:(kettle6,升级,kettle8)