引入Hive-16061补丁后遇到更严重的OOM问题

这个补丁用于修复Hive的某些输出不会打印到beeline控制台:
https://issues.apache.org/jira/browse/HIVE-16061

定位泄漏对象

将文件dump后容易发现泄漏的对象是这个Map
在这里插入图片描述
测试一条查询对应的key,成功remove掉了
在这里插入图片描述
客户端重新连接,新增了大量key,怀疑是新建客户端连接的时候没能成功移除这些key
在这里插入图片描述
同时磁盘上对应的文件夹却被删除掉,可以确认是这里发生了内存泄漏。

深入分析

首先不难发现

  • MAP是的私有对象,所以对它的操作一般要通过AbstractManager
  • MAP是静态的,同类对象之间共享
  • MAP引用不可变,不会有MAP新建替换的这种操作

但是作为remove依据的AbstractManager的name是私有的,这意味着:多个AbstractManager实现类共享着一个MAP,当自己的release()方法被调用的时候,根据自己的名字从MAP中remove掉。
在这里插入图片描述
从上篇分析来看,MAP中很多key没有被remove掉,是不是意味着这些AbstractManager的release()方法没被调用?
在这里插入图片描述
顺着这个思路看,AbstractOutputStreamAppender.stop()调用的时候会调用this.manager.release();

在这里插入图片描述

它的manager也是私有的,没有别的对象会操作这个,释放只发生再stop的时候

如果AbstractOutputStreamAppender的实现类被正确stop,那么manager也该被正确release(),MAP就没有这么多key;但事实相反,就表示AbstractOutputStreamAppender和AbstractManager的实现类和也被泄漏。
在这里插入图片描述

继续深入方法栈,可以看到具体的实现类
在这里插入图片描述
查看这个类的对象,可以发现实现类的确存在很多,而且应该是以前连接留下的
在这里插入图片描述
这个实现类是被LogUtils静态调用的,应该没有LogUtils发生泄漏。接着看方法栈会发现LogUtils是停止查询的Appender,这也能解释为什么之前查询的key能被remove掉。

在这里插入图片描述

目前来看只要找到连接结束时候的Appender,将其正确stop就可以解决这个问题。

省略大量断点调试的部分,找到了cleanupSessionLogDir方法

尝试修复

在HiveSessionImpl的cleanupSessionLogDir()中stop()这些对象,这样可以确保在文件无用的时候才stop()

sessionLogDir.list()可以拿到这些文件名

之前的分析已经看到LogUtils.stopQueryAppender(LogDivertAppender.QUERY_ROUTING_APPENDER, queryId)可以关闭这些

//stopQueryAppender 
String[] queryIdArr = sessionLogDir.list();
for(String queryId:queryIdArr) {
  LogUtils.stopQueryAppender(LogDivertAppender.QUERY_ROUTING_APPENDER, queryId);
}

提交编译,发布上线,重新测试,首次连接,发起查询来到问题出发的位置MAP.remove(this.name),发现有Map有27个kv,再次连接只有30个kv,并且只有这次会话的kv
在这里插入图片描述
再次测试,MAP依旧只有30个kv
在这里插入图片描述
但是RandomAccessFileAppender依旧有78个,三次会话分别为27、26、25个
在这里插入图片描述
可能是没有触发GC,需要GC后才消失。总之,至少MAP的泄漏问题得到了解决。

猜你喜欢

转载自blog.csdn.net/weixin_44112790/article/details/112317201