写点什么

Flink 任务类加载泄漏问题分析

作者:Joseph295
  • 2025-07-04
    北京
  • 本文字数:3031 字

    阅读完需:约 10 分钟

Exception in thread "Thread-3" java.lang.IllegalStateException: Trying to access closed classloader. Please check if you store classloaders directly or indirectly in static fields. If the stacktrace suggests that the leak occurs in a third party library and cannot be fixed immediately, you can disable this check with the configuration 'classloader.check-leaked-classloader'.		at org.apache.flink.util.FlinkUserCodeClassLoaders$SafetyNetWrapperClassLoader.ensureInner(FlinkUserCodeClassLoaders.java:189)		at org.apache.flink.util.FlinkUserCodeClassLoaders$SafetyNetWrapperClassLoader.getResource(FlinkUserCodeClassLoaders.java:219)		at org.apache.hadoop.conf.Configuration.getResource(Configuration.java:2861)		at org.apache.hadoop.conf.Configuration.getStreamReader(Configuration.java:3135)		at org.apache.hadoop.conf.Configuration.loadResource(Configuration.java:3094)		at org.apache.hadoop.conf.Configuration.loadResources(Configuration.java:3067)		at org.apache.hadoop.conf.Configuration.loadProps(Configuration.java:2945)		at org.apache.hadoop.conf.Configuration.getProps(Configuration.java:2927)		at org.apache.hadoop.conf.Configuration.get(Configuration.java:1265)		at org.apache.hadoop.conf.Configuration.getTimeDuration(Configuration.java:1886)		at org.apache.hadoop.conf.Configuration.getTimeDuration(Configuration.java:1863)		at org.apache.hadoop.util.ShutdownHookManager.getShutdownTimeout(ShutdownHookManager.java:183)		at org.apache.hadoop.util.ShutdownHookManager.shutdownExecutor(ShutdownHookManager.java:145)		at org.apache.hadoop.util.ShutdownHookManager.access$300(ShutdownHookManager.java:65)		at org.apache.hadoop.util.ShutdownHookManager$1.run(ShutdownHookManager.java:102)
复制代码


这个错误是由于 Flink 的类加载器管理机制检测到潜在的类加载器泄漏(特别是与 Hadoop 相关的库),在任务结束后尝试访问一个已经被关闭的类加载器导致的。根本原因在于 Hadoop 的 ShutdownHookManager 在 JVM 关闭钩子中尝试访问资源时,使用的类加载器(可能是包含 Hadoop 库的 Flink 用户代码类加载器)已经被 Flink 主动关闭了

原因分析:

  1. Flink 的类加载器生命周期管理: Flink 为每个任务(或作业)创建独立的 ClassLoader(用户代码类加载器)来加载用户 jar 包中的类和依赖。当任务成功执行完毕或失败终止后,Flink 会关闭(close这个类加载器,释放资源并防止类加载器泄漏。这是 Flink 资源管理的重要机制。

  2. Hadoop ShutdownHookManager 的静态引用: Hadoop 库(特别是 org.apache.hadoop.util.ShutdownHookManager)内部维护了一个 JVM 级别的静态(static 关闭钩子(Shutdown Hook)。这个钩子会在 JVM 退出时被触发,执行一些清理工作(如删除临时文件)。

  3. 冲突发生:

    当 Flink 任务(使用了 Hadoop 相关功能,如读写 HDFS)执行完毕,Flink 关闭了任务专属的用户代码类加载器(其中加载了 Hadoop 类)。

    稍后,当整个 JVM 进程(可能是 DolphinScheduler 的工作进程或 Flink 的 TaskManager 进程)准备退出时,JVM 会触发所有注册的关闭钩子。

    Hadoop 的 ShutdownHookManager 的关闭钩子开始执行。

    在执行钩子内部的逻辑(如 getShutdownTimeoutshutdownExecutor)时,需要加载或访问一些资源(如 Configuration 中的属性)。

    由于包含 Hadoop 类的原始类加载器(即那个 Flink 用户代码类加载器)已经被 Flink 提前关闭了,尝试通过这个已关闭的类加载器去访问资源就会抛出 IllegalStateException: Trying to access closed classloader

  4. Flink 的安全检查: Flink 使用 SafetyNetWrapperClassLoader 包装用户类加载器,并提供了 classloader.check-leaked-classloader 配置选项。当它检测到对已关闭类加载器的访问时,就会抛出此错误,帮助开发者发现潜在的类加载器泄漏问题(通常是静态字段持有类加载器引用导致其无法被 GC)。

解决办法:

根据你的环境和优先级,可以选择以下方案:

  1. 升级 Hadoop 依赖 (推荐,治本):

    核心问题修复: Hadoop 社区在较新版本(大约 Hadoop 3.3.0+ / 3.2.2+)中修复了这个问题(相关 issue 如 HADOOP-17112)。修复方式通常是让 ShutdownHookManager 避免依赖可能被卸载的类加载器中的资源。

    操作: 检查你的 Flink 任务使用的 Hadoop 依赖版本。如果版本较低(低于 3.2.2 或 3.3.0),将项目中的 Hadoop client 依赖升级到 3.3.x, 3.2.2+ 或更新的稳定版本。确保 Flink 运行环境(TaskManager 的 classpath)和你的用户 jar 包中的 Hadoop 版本一致且是修复后的版本。

  2. 在 Flink 配置中禁用类加载器泄漏检查 (临时/快速缓解):

    原理: 告诉 Flink 不要对访问已关闭类加载器的行为抛出异常。这不会解决类加载器泄漏的根本问题,但可以屏蔽这个由 Hadoop 已知问题引起的特定错误,让任务“正常”结束。注意:这可能会掩盖你代码中真正的类加载器泄漏问题!

    操作: 在提交 Flink 任务的配置中(通常在 flink-conf.yaml 或者在 DolphinScheduler 中配置 Flink 任务的 运行参数/其他参数 里),添加以下配置:

    yaml

    复制

    下载

    classloader.check-leaked-classloader: false

    DolphinScheduler 中位置:

    在定义 Flink 任务节点时,找到 自定义参数运行参数其他参数 或 Flink 高级参数 等类似的配置输入框。

    添加一行:-Dclassloader.check-leaked-classloader=false 或者直接添加配置项 classloader.check-leaked-classloader: false (取决于 DolphinScheduler 的 Flink 任务插件如何解析参数,通常 -D 的方式更通用)。

    例如,在 运行参数 里填写:

    text

    复制

    下载

    -yD classloader.check-leaked-classloader=false

    (Flink 命令行 -yD 用于设置动态属性)

  3. (备选) 排除/替换 Hadoop 依赖 (如果可行):

    如果你的 Flink 任务实际上并不需要 Hadoop 功能(例如,不读写 HDFS,不使用 YARN 作为资源管理器),检查是否误引入了 Hadoop 依赖。如果不需要,尝试在项目的构建文件(Maven/Gradle)中排除 Hadoop 依赖。

    如果任务只需要基础的 HDFS 访问,考虑使用 Flink 自带的 flink-hadoop-fs 模块(org.apache.flink:flink-hadoop-fs)提供的通用文件系统接口来替代直接依赖完整的 Hadoop client,它可能对类加载器更友好。但需要测试兼容性。

总结与建议:

  • 首选升级 Hadoop 客户端库到修复版本 (>= 3.2.2, >= 3.3.0)。 这是最根本、最安全的解决方案,解决了库本身的问题。

  • 如果立即升级 Hadoop 有困难,并且需要快速恢复任务运行,可以采用 classloader.check-leaked-classloader: false 配置项临时禁用检查。但请将此视为临时措施,并计划后续升级 Hadoop。

  • 仔细检查任务是否真的需要 Hadoop 依赖,如果不需要,排除它是更干净的做法。

  • 确保 Flink 运行环境(TaskManager classpath)和用户 jar 包中的 Hadoop 版本一致,避免版本冲突引发其他问题。

在 DolphinScheduler 中操作的关键点是在 Flink 任务节点的配置中找到合适的地方(通常是 运行参数自定义参数其他参数)添加 Flink 的配置项 classloader.check-leaked-classloader=false 或对应的命令行参数 -yD classloader.check-leaked-classloader=false

用户头像

Joseph295

关注

三脚猫的技术 2018-03-14 加入

coder

评论

发布
暂无评论
Flink 任务类加载泄漏问题分析_Joseph295_InfoQ写作社区