流计算 Oceanus | Flink JVM 内存超限的分析方法总结

作者:董伟柯,腾讯 CSIG 高级工程师

问题背景

前段时间,某客户线上运行的大作业(并行度 200 左右)遇到了 TaskManager JVM 内存超限问题(实际内存用量 4.1G > 容器设定的最大阈值 4.0G),被 YARN 的 pmem-check 机制检测到并发送了 SIGTERM(kill)信号终止该 container,最终导致作业出现崩溃。这个问题近期出现了好几次,客户希望能找到解决方案,避免国庆期间线上业务受到影响。

在 Flink 配置项中,提供了很多内存参数设定。我们逐一检查了客户作业的配置,发现各个内存配置的最大值之和也只有 3.75GB 左右(不含 JVM 自身 Native 内存区),离设定的 4.0GB 阈值还有 256MB 的空间。

用户作业并没有用到 RocksDB、GZip 等常见的需要使用 Native 内存且容易造成内存泄漏的第三方库,而且从 GC 日志来看,堆内各个区域远远没有用满,说明余量还是比较充足的。

那究竟是什么原因造成实际内存用量(RSS)超限了呢?

Flink 内存模型

要分析问题,首先要了解 Flink 和 JVM 的内存模型。官方文档 [1] 和很多第三方博客 [2] [3] 都对此有较为详尽的分析,这里只做流程的简单说明,不再详尽描述每个区域的具体计算过程。

下图展示了 Flink 内存各个区域的配置参数,其中左边是 Flink 配置项中的内存参数,中间是参数对应的内存区域,右边是这个作业配置的参数值。

在这里插入图片描述

图中深绿色的文字(taskmanager.memory.process.size)表示 JVM 所在容器内存的硬限制,例如 Kubernetes Pod YAML 的 resource limits。它的相关类为 ClusterSpecification,里面描述了 JobManager、TaskManager 容器所允许的最大内存用量,以及每个 TaskManager 的 Slot(运行槽)数等。

TaskManager 各个区域的内存用量是由 TaskExecutorProcessSpec 类来描述的。首先 Flink 的 ResourceManager 会调用 TaskExecutorFlinkMemoryUtils 工具类,从用户和系统的各项配置 Configuration 中获取各个内存区域的大小( TaskExecutorFlinkMemory 对象,不含 Metaspace 和 Overhead 部分)。这中间要考虑到旧版本参数的兼容性,所以有很多绕来绕去的封装代码。总而言之,优先级是 新配置 > 旧配置 > 无配置(计算推导 + 默认值)。随后再根据配置和上述的计算结果,推导出 JvmMetaspaceAndOverhead,最终封装为包含各个区域内存大小定义的 TaskExecutorProcessSpec 对象。

图中最右边浅绿色文字表示 Flink 内存参数最终翻译成的 JVM 参数(例如堆区域的 -Xmx、-Xms,Direct 内存区的 -XX:MaxDirectMemorySize 等),他们是 JVM 进程最终运行时的内存区域划分依据,是 ProcessMemoryUtils 这个工具类从上述的 TaskExecutorProcessSpec 对象中生成的。

堆内内存的分析

堆内内存(JVM Heap),指的是上图的 Framework Heap 和 Task Heap 部分。Task Heap 是 Flink 作业内存分配的重点区域,也是 JVM OutOfMemoryError: Java heap space 问题的发生地,当 OOM 问题发生时如下图:

在这里插入图片描述

如果这个区域内存占满了,也会出现不停的 GC,尤其是 Full GC。这些可以从监控指标面板看到,也可以通过 jstat 等命令查看。如果我们通过 Arthas、async-profiler [4] 等工具对 JVM 进行运行时火焰图采样的话,也可以看到类似下面的结果:GC 相关的线程占了很大的时间片比例:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值