Flink taskmanager metaspace OOM

现象

flink 程序打成jar多次提交后,有taskmanager 节点挂掉,这个现象而且经过多次试验后发现是必现,日志如下

The metaspace out-of-memory error has occurred. 
This can mean two things: either Flink Master requires jobmanager.memory.jvm-metaspace.size a larger size of JVM metaspace to load classes or there is a class loading leak. In the first case jobmanager.memory.jvm-metaspace.size  configuration option should be increased. 
If the error persists (usually in cluster after  several job (re-)submissions) then there is probably a class loading leak in user code or some of its dependencies which has to be investigated and fixed. The Flink Master has to be shutdown
复制代码

背景知识

flink里面加载器,分为 ParentFirstClassLoader ChildFirstClassLoader ,ChildFirstClassLoader是默认的类加载器

MetaSpace分析

从报错日志分析来看,一种是metaspace 真的非常小,另外的原因就是代码中存在内存泄漏,class 没有被回收,很明显问题应该在内存泄漏,metaspace这个问题应该还是出现的很少的。

微服务

通常的微服务通常启动过,就不会有新加载类的情况,所以很少出现此类问题,有些场景比如,部分用到反射的场景,可能会有这个问题

计算场景

计算平台每收到一个新的job,就要加载用户类,所以如果用户程序或者第三方jar有内存泄漏的情况就会导致task 节点的metaspace 一直增加,直到OOM

代码分析

代码逻辑 消费kafka--------->计算-------------->写es/kafka

写ES 用到的ES类

public RestHighLevelClient restHighLevelClient;
复制代码

步骤1

先简单修改下代码测试下

  1. 消费kafka--------->输出 重试N次,没有问题
  2. 消费kafka--------->计算----->输出 重试N次,没有问题
  3. 消费kafka--------->计算----->写es 问题复现
  4. 消费kafka--------->计算----->写kafka 重试N次,没有问题

经过上面的尝试 问题的方向应该在写es上

步骤2

查阅相关的资料

  1. 有没有任务里面有线程没有被关闭,这些线程可能还持有class 对象的引用
  2. 缓存相关的东西
  3. JDBC 相关的类,放在lib 目录下,这样只加载一次
  4. RuntimeContext.registerUserCodeClassLoaderReleaseHookIfAbsent() 注册一个钩子函数,手动释放掉类加载器

在这里插入图片描述

flink 调试类加载

步骤3

用MAT 工具分析 确实有很多类重复,每次提交相同的job,类就是会被加载,在测试的时候,用 jvisualvm 打开可以直观看到加载类的变化,也可以执行GC,这里要注意的是,一下要在多次GC之后,再dump,这样剩下的类才是没有办法卸载的 在这里插入图片描述

查看leak suspects 在这里插入图片描述 这里注意这个红色的,和ES相关,引起了我的注意

在这里插入图片描述

分别得到从这个对象到GC root的路径 在这里插入图片描述 GC root 到这个对象的最短路径 在这里插入图片描述 黄点表示GC ROOT path to GC roots merge shortest paths to GC roots 这两个的区别可以从图标上看出来 在这里插入图片描述

步骤4

从上面的分析再思考下,RestHighLevelClient关联的对象大小排第一,这个类应该在任务结束之后会被回收才对,为什么没有被回收呢?看下代码 RestHighLevelClient 对象确实有close方法,但是没有执行 修改下代码,重试下看看

 @Override
    public void close() throws Exception {
        super.close();
        RestHighLevelClient restHighLevelClient = esUtil.getRestHighLevelClient();
        restHighLevelClient.close();
    }
复制代码

步骤5

问题解决,提交N次任务,没有发生OOM 异常,多次提交任务后,taskmanager的metaspace 虽然会增加,但是触发full gc之后,类能够被卸载,metaspace 会减小

总结

虽然最后从文章来看,解决的比较容易,中间其实也看了很多的资料,最开始用MAT工具没有看出结果,下面是从这次问题可以收获的几个知识点

  1. 类卸载的时机
  2. classloader 卸载时机
  3. MAT 工具的使用
  4. jvisualvm 使用
  5. flink 类加载机制
  6. jvm 内存结构
  7. 解决问题的思路,比如可以不段缩小代码执行的范围

其它

之前这个问题一直都存在,一直没有解决,这次小长假的计划就是解决这个问题,最后的结果是完美的。

猜你喜欢

转载自juejin.im/post/7083083478136258591
OOM
今日推荐