1. 先建立一个直觉:Flink 的"内存"不是一块
你在日志里看到的内存异常,通常落在 5 类之一:
- Heap(Java 堆) :
OutOfMemoryError: Java heap space - Direct(JVM 直接内存 / off-heap direct) :
OutOfMemoryError: Direct buffer memory - Metaspace(类元数据区) :
OutOfMemoryError: Metaspace - Network buffer(网络 buffer 池) :
Insufficient number of network buffers - Container 总内存(容器/进程边界) :
Container Memory Exceeded/ OOMKilled
所以第一步永远是:别先加内存,先确认你缺的是哪一类。
2. 5 分钟排障流程(照抄就能用)
- 确认进程:是 TaskManager(TM)还是 JobManager(JM)报的
- 锁定异常类型:heap / direct / metaspace / network / container
- 对号入座参数:把异常映射到"最该动的那 1~3 个配置项"
- 排除配置冲突 :是否同时配置了
*.memory.process.size和*.memory.flink.size(或同时配了组件和总量) - 验证是否存在"非受控 native 内存":RocksDB、Python、Netty、第三方 SDK、JNI 等
下面逐个异常讲清楚"怎么看"和"怎么改"。
3. IllegalConfigurationException:启动直接失败
现象
IllegalConfigurationException 出自 TaskExecutorProcessUtils 或 JobManagerProcessUtils,Flink 直接起不来。
常见根因
- 配置值非法:负数、0、fraction > 1
- 配置冲突:同时显式设置了多套内存体系(例如 total process 和 total flink 同时配,或又配了 task heap + managed 还配总量)
- "按比例计算的组件"被 min/max 卡死:例如 JVM Overhead / Network memory 推导后落在范围外
快速处理清单
-
你在生产里建议只选一种主路径:
- 容器化(K8s/YARN):优先
taskmanager.memory.process.size/jobmanager.memory.process.size - Standalone:优先
taskmanager.memory.flink.size/jobmanager.memory.flink.size
- 容器化(K8s/YARN):优先
-
不要同时显式配置 total process 和 total flink
-
如果你走细粒度(task heap + managed),就不要再配 total process/total flink
4. OutOfMemoryError: Java heap space:堆不够(最常见)
现象
日志直接报 OutOfMemoryError: Java heap space,常伴随 GC 抽风、吞吐下降、反压越来越重。
怎么改最有效
-
TM(TaskManager)优先调:
taskmanager.memory.task.heap.size(保障算子/用户代码可用 heap)
-
JM(JobManager)优先调:
jobmanager.memory.heap.size(JM 的 heap)
-
如果你只想"整体扩大":提高 total memory(对应你选择的主路径)
taskmanager.memory.process.size或taskmanager.memory.flink.sizejobmanager.memory.process.size或jobmanager.memory.flink.size
什么时候才动 framework heap?
taskmanager.memory.framework.heap.size 是高级项,只在你确定 Flink 框架自身(高并行、复杂拓扑、依赖引入等)确实需要更多 heap 时再动,否则很容易"给框架加了,任务仍然 OOM"。
5. OutOfMemoryError: Direct buffer memory:直接内存不够 or 泄漏
现象
OutOfMemoryError: Direct buffer memory
典型根因
- JVM Direct memory 上限太小(TM 更常见)
- 你的用户代码/依赖偷偷用 direct(Netty、NIO、某些客户端、压缩库等),但没被你在 Flink 的内存模型里"算进去"
- 直接内存泄漏(少见但杀伤力大)
推荐操作
-
优先确保 off-heap 的预算被正确计入:
taskmanager.memory.task.off-heap.size(用户代码 off-heap 预算)taskmanager.memory.framework.off-heap.size(框架 off-heap,谨慎调)
-
如果你是在容器里跑:确认
taskmanager.memory.process.size足够覆盖 native 需求,否则会变成"容器被杀"而不是 OOM
小提示
Flink 会根据内存组件推导 JVM 参数(包括 -XX:MaxDirectMemorySize),你调 off-heap 的本质是在影响这个上限与预算结构。
6. OutOfMemoryError: Metaspace:类太多 / metaspace 太小
现象
OutOfMemoryError: Metaspace
常见场景
- 动态类加载多:UDF、反射、某些框架、shade 依赖复杂
- metaspace 上限过小
直接修复
-
调整 metaspace:
- TM:
taskmanager.memory.jvm-metaspace.size - JM:
jobmanager.memory.jvm-metaspace.size
- TM:
如果你是"突然 metaspace 爆炸",建议顺手排查是否出现了 classloader 泄漏(比如不停创建新的 UDF/ClassLoader)。
7. IOException: Insufficient number of network buffers:网络 buffer 不够
现象
IOException: Insufficient number of network buffers(只跟 TM 有关)
根因
Network memory 配小了,shuffle / 数据交换 buffer 池不够用。
对应参数(3 选 1~2)
taskmanager.memory.network.fractiontaskmanager.memory.network.mintaskmanager.memory.network.max
经验写法
你可以先不动 fraction,只把 min/max 拉开一点,避免推导结果被卡死。例如(示意):
yaml
taskmanager.memory.network.min: 256mb
taskmanager.memory.network.max: 1024mb
# fraction 不改或小幅调整
# taskmanager.memory.network.fraction: 0.1
8. Container Memory Exceeded / OOMKilled:容器边界被打爆(最隐蔽)
现象
YARN/K8s 提示容器超内存,直接 kill。你可能在 Flink 日志里看不到传统 OOM,只看到进程被干掉。
根因
Flink 没有为 native/off-heap 留够空间,导致进程总占用超过容器 request/limit。常见触发者:
- RocksDB native 内存(state 特别大时更明显)
- glibc 内存分配器导致的碎片/arena 膨胀
- 依赖库在 native 侧大量申请但未纳入预算
JM 侧一个实用开关
如果问题发生在 JM,你可以启用 JVM Direct Memory limit(用于排除/约束 direct 泄漏路径):
jobmanager.memory.enable-jvm-direct-memory-limit: true
RocksDB 相关(重点)
如果你用的是 RocksDB StateBackend(或 EmbeddedRocksDBStateBackend):
- 如果 RocksDB memory controlling 被禁用
优先:增加 TM 的 managed memory
taskmanager.memory.managed.size(或提高 managed fraction)
- 如果 memory controlling 启用,但在 savepoint / 全量 checkpoint 期间 non-heap 飙升
这可能是 glibc allocator 的 "arena" 问题。一个非常常用的止血手段是给 TM 加环境变量:
yaml
env:
java:
opts:
taskmanager: ""
# 具体在 K8s/YARN 的环境变量配置里加:
# MALLOC_ARENA_MAX=1
如果你不方便改环境变量,另一条路是 增加 JVM Overhead(给线程栈、GC、native 杂项留空间):
taskmanager.memory.jvm-overhead.min/max/fraction
9. 一张"报错 → 改哪儿"的速查表
-
IllegalConfigurationException
- 先查冲突:别同时配 total process 和 total flink
- 再查 min/max/fraction 是否越界
-
Java heap space
- TM:
taskmanager.memory.task.heap.size或提高 total memory - JM:
jobmanager.memory.heap.size或提高 total memory
- TM:
-
Direct buffer memory
- TM:
taskmanager.memory.task.off-heap.size(必要时框架 off-heap) - 容器里:提高
taskmanager.memory.process.size,否则会直接 OOMKilled
- TM:
-
Metaspace
taskmanager.memory.jvm-metaspace.size/jobmanager.memory.jvm-metaspace.size
-
Insufficient network buffers
taskmanager.memory.network.min/max/fraction
-
Container Memory Exceeded
- 优先走
*.memory.process.size思路(容器化) - RocksDB:加 managed 或处理 glibc(
MALLOC_ARENA_MAX=1) - 必要时加 JVM Overhead
- 优先走
10. 结尾:调内存的"正确姿势"
- 先按异常类型对症下药,不要一上来就把 process size 翻倍
- 容器化部署优先用
*.memory.process.size,让 Flink 的推导和容器边界对齐 - RocksDB/Netty/第三方 SDK 的 native 内存,是"容器被杀"的头号嫌疑人
- 任何一次调整都要留意"推导后的组件是否被 min/max 卡死",否则就是 IllegalConfigurationException