1、IllegalConfigurationException:配置值不合法或互相冲突
典型特征
- 异常来自
TaskExecutorProcessUtils或JobManagerProcessUtils - 常见提示:负数内存、fraction > 1、min/max 冲突、组件组合不成立等
常见原因
- 内存值写成了负数或 0(例如
-1m、0m) - fraction 配置超范围(例如
taskmanager.memory.network.fraction > 1) - min/max 逻辑错误:
min > max - 同时显式指定了互斥的内存项(比如既指定总进程内存又把若干子项写死到无法满足)
怎么排
- 直接搜异常信息里提到的内存组件名(network/managed/overhead/metaspace...)
- 检查 fraction 是否在合理范围(通常 0~1)
- 检查 min/max 是否满足
min <= max - 如果你同时配了"总内存"和多个"子内存",先尝试只保留总内存,让 Flink 自动推导;或者只保留关键子项,其余交给推导
2、OutOfMemoryError: Java heap space:JVM 堆不够(对象太多或太大)
典型场景
- 大量对象创建(JSON 解析、拼接字符串、Map/List 聚合)
- 算子里缓存数据、窗口太大、状态反序列化到堆
- Sink/Source 端 batch 缓冲写堆里
怎么解决(按优先级)
- 增加总内存:整体给 TaskManager/JobManager 更多 process size(最省事)
- 更精准:增加 TaskManager 的 task heap 或 JobManager 的 JVM heap
- 只有在你确信是框架自身需要更多堆时,再调整 framework heap(一般不建议先动)
常用配置方向(示例)
yaml
# 方案A:直接加总进程内存(推荐先从这里试)
taskmanager.memory.process.size: 8192m
jobmanager.memory.process.size: 2048m
# 方案B:更精细地加堆(你确定堆才是瓶颈时)
taskmanager.memory.task.heap.size: 4096m
jobmanager.memory.heap.size: 1024m
排查小技巧:Heap OOM 往往伴随 GC 时间暴涨、吞吐下降、延迟上升。先看 GC 日志/指标,比"盲加内存"更快。
3、OutOfMemoryError: Direct buffer memory:Direct/Off-Heap 不够或泄漏
典型场景
- Netty / NIO direct buffer 占用过大
- 依赖库(例如某些客户端、序列化库、压缩库)大量用 direct memory
- 真泄漏:申请了 direct buffer 没释放或释放不及时
怎么解决
- 提高 direct/off-heap 额度:让 direct memory 上限更大
- 排查依赖是否使用 direct memory:尤其是自定义连接器、客户端 SDK
- 如果怀疑某进程存在 direct leak,可以考虑启用 Flink 对 JVM Direct Memory limit 的控制(文档里提到 JobManager 可用该开关来排除 leak 干扰)
你要避免的误区:Direct OOM 不是 heap OOM,加大 heap 可能一点用都没有。
4、OutOfMemoryError: Metaspace:类元数据区不够(类太多或类加载泄漏)
典型场景
- 依赖非常多、Shade 后体积巨大
- UDF 动态加载频繁、ClassLoader 没回收(尤其是某些反射/脚本场景)
- 频繁提交作业、频繁重启导致类加载压力大
解决
- 增加 metaspace 配额(TaskManager 或 JobManager 对应项)
- 如果 metaspace 持续上涨而不回落,重点怀疑 ClassLoader 泄漏或动态生成类(字节码增强、脚本引擎等)
5、IOException: Insufficient number of network buffers:网络内存不足(只影响 TaskManager)
典型场景
- 上游并发/分区数很高,shuffle 压力大
- 反压明显,网络 buffer 被占满
- 网络内存 fraction 太小,min/max 太保守
对应配置就三件套
taskmanager.memory.network.mintaskmanager.memory.network.maxtaskmanager.memory.network.fraction
建议的排查顺序
- 先看当前并发和分区数是不是"飙了"(比如 keyBy 后突然高分区)
- 再看 network memory 是不是被 fraction 限死(max 太小)
- 逐步增大 network min/max 或 fraction,并观察吞吐、反压是否缓解
6、Container Memory Exceeded:容器被 Yarn/K8S 杀掉(Native 内存没预留够)
这是最"冤"的一种:你以为配了 8G,实际 JVM + direct + metaspace + overhead + native 叠起来超过了容器 limit,K8S/Yarn 直接 OOMKill。
核心结论
- 这类问题通常不是"堆不够",而是 native / overhead 预留不足,导致进程总占用超过容器申请值。
处理思路
-
确认容器真实限制(K8S limits / Yarn container memory)是多少
-
让 Flink 的 process size 与容器限制对齐,并确保 overhead、direct、managed 等推导后不会顶穿容器
-
如果是 JobManager 进程遇到该问题,并且怀疑 direct memory 泄漏干扰定位,可以启用:
jobmanager.memory.enable-jvm-direct-memory-limit
涉及 RocksDBStateBackend 时的特殊情况
-
如果 RocksDB 内存控制关闭:优先尝试增加 TaskManager managed memory(RocksDB 很吃这一块)
-
如果 RocksDB 内存控制开启,且在 savepoint/全量 checkpoint 时非堆内存飙升:可能是 glibc 内存分配器行为导致(典型就是 arena 太多)
- 给 TaskManager 增加环境变量:
MALLOC_ARENA_MAX=1 - 或者增大 JVM Overhead(给 native 波峰留缓冲)
- 给 TaskManager 增加环境变量:
一套通用的排障流程(建议你收藏)
- 锁定异常类型:heap / direct / metaspace / network / container kill
- 确认发生在谁身上:JobManager 还是 TaskManager(network buffer 只在 TM)
- 收集三份信息:Flink 配置、容器限制、任务并发与状态规模
- 只调一类参数:一次只针对一个桶加内存,避免把问题搅浑
- 用指标验证:看 GC、direct 使用、network buffer、checkpoint 峰值、容器 RSS
- 再谈优化:确认不是代码层面无界缓存、窗口过大、状态膨胀、反压导致的连锁问题
结尾:别把"加内存"当万能药
- Heap OOM 就加 heap/总内存
- Direct OOM 就加 off-heap/direct 或查 direct leak
- Metaspace OOM 就加 metaspace 并排查类加载
- Network buffer 不够就调 network 三件套
- 容器超限就补 native/overhead 预留,并对齐容器限制