Flink 內(nèi)存模型原來是這樣設(shè)計的,漲姿勢了
今天我們聊聊 Apache Flink 的內(nèi)存模型。
一、Flink 內(nèi)存模型的核心目標(biāo)
Flink的內(nèi)存模型就是一套對 JobManager和TaskManager 進(jìn)程內(nèi)存進(jìn)行結(jié)構(gòu)化劃分和管理的機(jī)制。它的核心思想是:把內(nèi)存分成多個明確用途的部分,每部分獨立配置、獨立管理,避免互相干擾。

二、TaskManager 的整體內(nèi)存結(jié)構(gòu)
在 Flink 中,每個 TaskManager 是一個 JVM 進(jìn)程。這個進(jìn)程的總內(nèi)存(Total Process Memory)被劃分為兩大塊:
- JVM 相關(guān)的內(nèi)存(JVM Overhead)
- Flink 管理的內(nèi)存(Flink Memory)
1. JVM 相關(guān)的內(nèi)存(JVM Overhead)
這部分不是 Flink 直接使用的,而是 JVM 自身運行所需的內(nèi)存,包括:
- JVM 元空間(Metaspace)
- 線程棧(Thread Stacks)
- 代碼緩存(Code Cache)
- 直接內(nèi)存(Direct Memory)中非 Flink 控制的部分
- GC 開銷等
這部分內(nèi)存由 Flink 通過 jvm-overhead-fraction 或 jvm-overhead-min/max 配置,默認(rèn)占總進(jìn)程內(nèi)存的 20%(但不低于 192MB,不高于 1GB)。
2. Flink 管理的內(nèi)存(Flink Memory)
這是 Flink 實際用來執(zhí)行任務(wù)的內(nèi)存,又被進(jìn)一步細(xì)分為:
- 堆內(nèi)內(nèi)存(On-heap)
- Task Heap Memory(任務(wù)堆內(nèi)存)
- Network Memory(網(wǎng)絡(luò)內(nèi)存,可選堆內(nèi))
- 堆外內(nèi)存(Off-heap)
- Managed Memory(托管內(nèi)存)
- Network Memory(網(wǎng)絡(luò)內(nèi)存,通常堆外)
- Task Off-Heap Memory(任務(wù)堆外內(nèi)存)
注意:Network Memory 可以配置為堆內(nèi)或堆外,但默認(rèn)是堆外。

三、Flink Memory 的詳細(xì)組成
Flink Memory = Total Flink Memory
而 Total Flink Memory = Task Heap + Task Off-Heap + Managed Memory + Network Memory
下面我們逐個解釋這四部分。

1. Task Heap Memory(任務(wù)堆內(nèi)存)
用途:這是 TaskManager 中所有算子(operator)和用戶代碼(如 map、filter、window 函數(shù))運行時使用的 JVM 堆內(nèi)存。
特點:
- 屬于 JVM 堆(on-heap)。
- 由 JVM 垃圾回收器管理。
- 如果你的業(yè)務(wù)邏輯創(chuàng)建了很多對象(比如 POJO、中間結(jié)果),它們就存在這里。
配置方式:
- 主要通過 taskmanager.memory.task.heap.size 顯式設(shè)置。
- 或者通過總內(nèi)存反推(見后文“內(nèi)存配置策略”)。

?? 注意:不要把“Task Heap”和整個 JVM 堆混淆。JVM 堆還包括 Flink 內(nèi)部框架對象、網(wǎng)絡(luò)緩沖區(qū)(如果設(shè)為堆內(nèi))等,但 Flink 把“用戶任務(wù)可用的堆內(nèi)存”單獨劃出來叫 Task Heap。
2. Task Off-Heap Memory(任務(wù)堆外內(nèi)存)
用途:供用戶代碼或算子使用的堆外內(nèi)存。
典型場景:
- 使用 RocksDB 狀態(tài)后端時,RocksDB 會分配大量堆外內(nèi)存。
- 用戶自定義函數(shù)中調(diào)用 ByteBuffer.allocateDirect()。
特點:
- 不受 JVM GC 管理。
- 需要手動釋放(但 RocksDB 等庫會自己管理)。
配置:通過 taskmanager.memory.task.off-heap.size 設(shè)置,默認(rèn)為 0。

建議:如果你用 RocksDB,一定要給 Task Off-Heap 分配足夠內(nèi)存,否則可能 OOM。
3. Managed Memory(托管內(nèi)存)
用途:Flink 內(nèi)部用于特定算子的批處理優(yōu)化操作,例如:
- 排序(Sort)
- 哈希表(Hash Join / Hash Aggregate)
- 緩存中間結(jié)果(如批模式下的 shuffle buffer)
關(guān)鍵特性:
- 默認(rèn)是堆外內(nèi)存(off-heap),但可通過 taskmanager.memory.managed.memory-off-heap 改為堆內(nèi)。
- 大小可以按 Task Slot 數(shù)量比例分配(即每個 slot 獲得固定份額)。
- 在流模式下,Managed Memory 通常用得少;在批模式(Batch Execution)下非常重要。
配置方式:
- 可以直接指定大?。簍askmanager.memory.managed.size
- 或按比例:taskmanager.memory.managed.fraction(默認(rèn) 0.4,即占 Total Flink Memory 的 40%)

?? 提示:如果你跑的是純流作業(yè)且不用排序/Join,可以適當(dāng)調(diào)低 Managed Memory,騰出內(nèi)存給其他部分。
4. Network Memory(網(wǎng)絡(luò)內(nèi)存)
用途:用于 TaskManager 之間的數(shù)據(jù)傳輸緩沖區(qū)(network buffers)。
- 包括上游發(fā)送數(shù)據(jù)的輸出緩沖區(qū)(ResultPartition)
- 和下游接收數(shù)據(jù)的輸入緩沖區(qū)(InputGate)
為什么重要:
- 網(wǎng)絡(luò)緩沖區(qū)不足會導(dǎo)致背壓(backpressure),降低吞吐。
- 緩沖區(qū)太多又浪費內(nèi)存。
實現(xiàn)細(xì)節(jié):
- 每個緩沖區(qū)默認(rèn) 32KB(可通過 taskmanager.memory.segment-size 調(diào)整)。
- 緩沖區(qū)數(shù)量由并行度、網(wǎng)絡(luò)連接數(shù)決定。
內(nèi)存位置:
- 默認(rèn)是堆外(off-heap),因為堆外內(nèi)存更適合高頻 IO,避免 GC 干擾。
- 可通過 taskmanager.memory.enable-jvm-direct-memory-limit 等參數(shù)控制。
配置方式:
不能直接指定大小,而是通過范圍約束:
- taskmanager.memory.network.min(默認(rèn) 64MB)
- taskmanager.memory.network.max(默認(rèn) 1GB)
- Flink 啟動時根據(jù)實際網(wǎng)絡(luò)需求,在 min 和 max 之間動態(tài)計算所需大小。

?? 如果作業(yè)并行度很高或 shuffle 數(shù)據(jù)量大,務(wù)必確保 network.max 足夠大,否則啟動失?。▓箦e:“Insufficient network memory”)。
四、JobManager 的內(nèi)存模型(簡要)
雖然問題聚焦 TaskManager,但提一句 JobManager:JobManager 內(nèi)存模型簡單得多。
主要包括:
- JVM Heap(用于調(diào)度、checkpoint 協(xié)調(diào)、元數(shù)據(jù)存儲)
- JVM Overhead
配置項如 jobmanager.memory.heap.size、jobmanager.memory.off-heap.size(用于某些 RPC 或 HA 狀態(tài))。
一般不需要像 TaskManager 那樣精細(xì)調(diào)優(yōu)。
五、內(nèi)存配置的兩種策略
Flink 1.18 支持兩種配置內(nèi)存的方式,你只能選一種:
方式一:從總進(jìn)程內(nèi)存出發(fā)(推薦生產(chǎn)環(huán)境使用)
你指定 TaskManager 進(jìn)程的總內(nèi)存(taskmanager.memory.process.size),F(xiàn)link 自動按規(guī)則拆分各部分。
例如:
taskmanager.memory.process.size: 4096mFlink 會:
(1) 先預(yù)留 JVM Overhead(比如 20%,即 819MB)
(2) 剩下 3277MB 作為 Total Flink Memory
(3) 再從中分配:
- Managed Memory:40% → ~1310MB
- Network Memory:根據(jù)網(wǎng)絡(luò)需求,在 64MB~1GB 之間動態(tài)定(假設(shè) 512MB)
- Task Off-Heap:默認(rèn) 0
- Task Heap = 剩余部分 ≈ 3277 - 1310 - 512 = 1455MB

優(yōu)點:簡單,適合容器化部署(如 Kubernetes),因為你只需要告訴 K8s 分配多少內(nèi)存給 Pod。
方式二:顯式指定各部分內(nèi)存
你可以分別設(shè)置:
taskmanager.memory.task.heap.size: 2048m
taskmanager.memory.managed.size: 1024m
taskmanager.memory.network.min: 256m
taskmanager.memory.network.max: 512m
taskmanager.memory.task.off-heap.size: 256mFlink 會據(jù)此計算 Total Flink Memory = 2048 + 0(off-heap 默認(rèn)0)+ 1024 + 網(wǎng)絡(luò)內(nèi)存(取實際值),再加 JVM Overhead 得到總進(jìn)程內(nèi)存。

優(yōu)點:控制精確,適合性能調(diào)優(yōu)。
? 注意:兩種方式不能混用。一旦你設(shè)置了 process.size,就不能再設(shè) task.heap.size 等;反之亦然。Flink 會報配置沖突錯誤。
六、常見誤區(qū)澄清
誤區(qū)1:“Managed Memory 是給狀態(tài)后端用的”
錯。狀態(tài)后端(State Backend)使用的內(nèi)存不屬于 Managed Memory。
- Heap State Backend:狀態(tài)存在 Task Heap 中。
- RocksDB State Backend:狀態(tài)存在磁盤,但緩存和索引用的是 Task Off-Heap Memory(或操作系統(tǒng) page cache)。
Managed Memory 只用于批處理中的臨時數(shù)據(jù)結(jié)構(gòu)(如排序緩沖區(qū))。
誤區(qū)2:“Network Memory 越大越好”
不一定。過大的 Network Memory 會擠占 Task Heap 或 Managed Memory,反而導(dǎo)致算子 OOM。應(yīng)根據(jù)實際 shuffle 量合理設(shè)置 max。
誤區(qū)3:“Flink 內(nèi)存都在 JVM 堆里”
錯。Flink 大量使用堆外內(nèi)存(Network、Managed、RocksDB),這是為了減少 GC 壓力和提升 IO 性能。
七、如何查看實際內(nèi)存分配?
啟動 Flink TaskManager 后,日志中會打印詳細(xì)的內(nèi)存分配信息,例如:
TaskManager memory configuration:
Total Process Memory: 4096.000mb
Total Flink Memory: 3276.800mb
- Task Heap Memory: 1455.000mb
- Task Off-Heap Memory: 0.000mb
- Managed Memory: 1310.720mb
- Network Memory: 511.080mb
JVM Overhead Memory: 819.200mb這是驗證配置是否生效的最直接方式。
八、調(diào)優(yōu)建議(實戰(zhàn)角度)
(1) 流作業(yè) + RocksDB:
- 增加 taskmanager.memory.task.off-heap.size(比如 1~2GB)
- 可適當(dāng)降低 managed.fraction(如 0.2),因為流作業(yè)很少用 Managed Memory
(2) 批作業(yè)(Batch SQL / DataSet):
- 確保 managed.fraction 足夠(默認(rèn) 0.4 通常 OK)
- 監(jiān)控是否因 Managed Memory 不足觸發(fā) spill to disk(性能下降)
(3) 高并行度作業(yè):
- 提高 taskmanager.memory.network.max(如 2GB)
- 否則可能因網(wǎng)絡(luò)緩沖區(qū)不足無法啟動
(4) 容器化部署(K8s / YARN):
- 使用 process.size 配置
- 確保容器內(nèi)存限制 ≥ process.size + 安全余量(防止被 kill)
(5) 避免 Full GC:
- 控制 Task Heap 不要過大(建議單個 TM ≤ 4GB)
- 更大內(nèi)存需求用多個 TaskManager 分?jǐn)?/li>
九、總結(jié):Flink 1.18 內(nèi)存模型的關(guān)鍵點
內(nèi)存區(qū)域 | 用途 | 是否受 GC | 默認(rèn)位置 | 配置參數(shù) |
Task Heap | 用戶算子代碼對象 | 是 | 堆內(nèi) |
|
Task Off-Heap | RocksDB / 用戶堆外 | 否 | 堆外 |
|
Managed Memory | 批處理排序/Join 緩沖 | 否(默認(rèn)) | 堆外 |
或 |
Network Memory | 網(wǎng)絡(luò)數(shù)據(jù)傳輸緩沖 | 否(默認(rèn)) | 堆外 |
|
JVM Overhead | JVM 自身開銷 | — | — |
|
Flink 1.18 的內(nèi)存模型本質(zhì)是一套聲明式、模塊化、可預(yù)測的內(nèi)存分配機(jī)制。它讓你明確知道每一塊內(nèi)存干什么、怎么配、會不會沖突。掌握這套模型,就能避免大多數(shù) OOM 和性能問題。




























