您好:
请看附件分析是否正确?
王晓华
[email protected]
# Doris FE OOM Heap Dump 分析报告
## 一、宕机概况
| 项目 | 详情 |
|------|------|
| **FE版本** | Doris 3.1.4 |
| **JVM配置** | `-Xmx32768m -Xms32768m`(32GB堆) |
| **Java版本** | OpenJDK 17.0.12 |
| **宕机时间** | 2026-08-17 ~10:54 |
| **FE启动时间** | 2026-08-03 16:05(运行约14天) |
| **宕机原因** | `java.lang.OutOfMemoryError: Java heap space` |
| **Heap Dump** | 30.46GB,545,178,838个对象 |
| **重启后堆配置** | 48GB(`-Xmx49152m`) |
### fe.out 关键输出
```
java.lang.OutOfMemoryError: Java heap space
Dumping heap to /data01/doris/fe/log/java_pid72774.hprof ...
Heap dump file created [30463119875 bytes in 98.764 secs]
#
# java.lang.OutOfMemoryError: Java heap space
# -XX:OnOutOfMemoryError="kill -9 %p"
# Executing /bin/sh -c "kill -9 72774"...
```
## 二、MAT分析结果
使用 Eclipse Memory Analyzer (MAT) 对 29GB Heap Dump
进行自动化分析,识别出**两个泄漏嫌疑点**,均指向同一线程 `all-fe-session-mgr-pool-0`。
### 2.1 Problem Suspect 1:String 对象堆积(17.3GB,57.64%)
- **180,268,901** 个 `java.lang.String` 实例,占用 **17,305,670,752
字节(17.3GB,57.64%)**
- 被一个 `Object[180,221,098]` 数组引用(1.44GB)
- 该数组由线程 `all-fe-session-mgr-pool-0` 持有
### 2.2 Problem Suspect 2:ConcurrentHashMap 膨胀(10.8GB,35.97%)
- 一个 `ConcurrentHashMap$Node[268,435,456]`(2^28 个桶)占用 **10,800,711,176
字节(10.8GB,35.97%)**
- 包含 **180,239,068** 个 Node 条目
- 同样由线程 `all-fe-session-mgr-pool-0` 持有
### 2.3 OOM 触发时的线程栈
```
at java.lang.OutOfMemoryError.<init>() (OutOfMemoryError.java:48)
at java.util.Arrays.copyOf() (Arrays.java:3481)
at java.util.concurrent.ConcurrentHashMap$CollectionView.toArray()
(ConcurrentHashMap.java:4471)
at java.util.ArrayList.<init>() (ArrayList.java:181)
at org.apache.doris.catalog.Env.getAllAliveSessionIds() (Env.java:7031)
at org.apache.doris.catalog.FESessionMgr$FEAliveSessionHandler.run()
(FESessionMgr.java:94)
at java.util.concurrent.Executors$RunnableAdapter.call() (Executors.java:539)
at java.util.concurrent.FutureTask.run() (FutureTask.java:264)
at java.util.concurrent.ThreadPoolExecutor.runWorker()
(ThreadPoolExecutor.java:1136)
at java.util.concurrent.ThreadPoolExecutor$Worker.run()
(ThreadPoolExecutor.java:635)
at java.lang.Thread.run() (Thread.java:842)
```
### 2.4 MAT Top Consumers(支配树最大对象)
| 对象类型 | 实例数 | Shallow Heap | Retained Heap | 占比 |
|---------|--------|-------------|---------------|------|
| `ConcurrentHashMap$Node[]` | 5 | 2,147,483,672 | 10,806,532,600 | 35.99% |
| `java.lang.Thread` (all-fe-session-mgr-pool-0) | 1 | 160 | 1,441,778,504 |
4.80% |
| `java.lang.String` | 180,268,901 | 5,768,604,832 | 17,305,670,752 | 57.64% |
### 2.5 MAT Class Histogram(Top 10)
| 类名 | 对象数 | Shallow Heap | Retained Heap |
|------|--------|-------------|---------------|
| `java.lang.String` | 180,549,648 | 5,777,588,736 | >=17,339,850,056 |
| `byte[]` | 180,690,113 | 11,773,285,544 | >=11,773,285,544 |
| `ConcurrentHashMap$Node[]` | 25,884 | 2,151,264,016 | >=10,849,355,368 |
| `ConcurrentHashMap$Node` | 180,353,465 | 8,656,966,320 | >=8,678,776,232 |
| `java.lang.Object[]` | 96,712 | 1,471,594,280 | >=1,568,369,392 |
| `java.lang.Thread` | 448 | 71,680 | >=1,447,339,640 |
| `com.sleepycat.je.tree.BIN` | 1,054 | 278,256 | >=106,519,560 |
| `com.sleepycat.je.tree.IN` | 1,062 | 25,488 | >=103,781,928 |
| `com.sleepycat.je.tree.Node[]` | 1,075 | 1,113,704 | >=103,757,984 |
| `com.sleepycat.je.tree.LN` | 132,262 | 4,232,384 | >=102,644,392 |
### 2.6 MAT 系统概览
| 项目 | 值 |
|------|-----|
| Heap Dump 大小 | 28 GB |
| 对象总数 | 545,178,838 |
| 类总数 | 27,765 |
| 类加载器总数 | 309 |
| GC Roots | 4,316 |
| 不可达对象 | 170,284(仅21MB) |
> **关键发现**:545M 对象中仅有 170,284 个不可达对象(21MB),几乎所有对象都是存活且无法回收的泄漏对象。
## 三、根因分析
### 3.1 根本原因:FE Session ID 泄漏
`FESessionMgr` 中的 `aliveSessionIds` ConcurrentHashMap 持续累积了约 **1.8 亿个 Session
ID**,从未被有效清理。
### 3.2 泄漏机制详解
```
客户端高频创建连接
|
v
每次连接产生一个 Session ID,存入 aliveSessionIds (ConcurrentHashMap)
|
v
连接关闭后 Session ID 仍留在 map 中,等待定期清理
|
v
清理线程 all-fe-session-mgr-pool 队列满,清理任务被拒绝 (RejectedExecutionException)
|
v
FE 间同步也失败 (Socket is closed by peer / Connection refused)
|
v
Session ID 只增不减,14天累积 1.8 亿条目
|
v
FEAliveSessionHandler 定期调用 Env.getAllAliveSessionIds()
-> new ArrayList(sessionIds.keySet()) <-- 1.8亿条目全量拷贝
-> OOM
```
### 3.3 内存分布
| 对象类型 | 实例数 | 内存占用 | 占比 |
|---------|--------|---------|------|
| String(Session ID) | 180,549,648 | 17.3GB | 57.64% |
| ConcurrentHashMap$Node[] | 5 | 10.8GB | 35.99% |
| ConcurrentHashMap$Node | 180,353,465 | 8.7GB | (含在上面) |
| **合计** | ~545M | **~28GB** | **93.6%** |
### 3.4 清理机制失效时间线
| 日期 | all-fe-session-mgr-pool 队列满次数 | 说明 |
|------|-------------------------------------|------|
| 8/12 | 0 | 正常 |
| 8/13 | 0 | 正常 |
| **8/14** | **8** | 首次出现清理任务被拒绝 |
| 8/15 | 6 | 持续恶化 |
| 8/16 | 4 | 持续恶化 |
| **8/17** | **27** | 密集爆发,最终OOM |
### 3.5 完整事件时间线
| 时间 | 事件 |
|------|------|
| **8/3 16:05** | FE启动,32GB堆 |
| **8/14** | 首次出现 `RejectedExecutionException: queue size is full:
all-fe-session-mgr-pool`(8次) |
| **8/15-16** | 队列满错误持续(6次、4次) |
| **8/17 09:09** | 队列满错误密集爆发 |
| **8/17 09:18** | Full GC后残留内存已达24GB,无法回收 |
| **8/17 10:42-10:55** | 连续 Full GC,每次耗时20+秒,GC后残留25-27GB |
| **8/17 10:54:17** | `PUBLISH_VERSION_EXEC` 写BDB耗时19305ms,锁持有18.4秒 |
| **8/17 ~10:54** | `Env.getAllAliveSessionIds()` 触发 OOM |
| **8/17 10:55:29** | 最后一次 Full GC:27GB存活对象,GC耗时21秒 |
| **8/17 10:57** | Heap Dump 生成完毕(30.46GB) |
| **8/17 10:57** | `kill -9` 终止进程 |
### 3.6 GC 日志分析
```
8/17 10:42:45 Full GC: 32402M -> 24621M (2572ms) <- Full GC后仍残留24GB
8/17 10:43:37 Full GC: 31488M -> 25845M (3236ms)
8/17 10:44:27 Full GC: 27244M -> 25830M (3097ms)
...
8/17 10:54:48 Full GC前: 27134388088 bytes (~27GB)
8/17 10:54:56 Full GC后: 27145836592 bytes (~27GB) <- 几乎无法回收
8/17 10:55:07 Full GC后: 27134575736 bytes (~27GB)
8/17 10:55:21 Full GC后: 27134388088 bytes (~27GB) <- 最后一次,21秒
```
Full GC 后仅能回收几百MB,27GB为存活无法回收的泄漏对象。
## 四、业务负载分析
### 4.1 审计日志统计(8/17当天)
| 语句类型 | 数量 |
|---------|------|
| TRANSACTION | 99,706 |
| SELECT | 80,998 |
| SET | 36,985 |
| INSERT | 8,290 |
| SHOW | 7,584 |
| CREATE | 1,817 |
| DELETE | 483 |
### 4.2 用户请求分布
| 用户 | INSERT/TRANSACTION 数 | 占比 |
|------|----------------------|------|
| **prod_qd_ab_wxh_w** | 101,911 | **95.5%** |
| prod_cd_option_w | 3,353 | 3.1% |
| test_cd_option_w | 2,603 | 2.4% |
| admin | 121 | 0.1% |
| prod_cd_raven_w | 8 | <0.1% |
### 4.3 prod_qd_ab_wxh_w 语句分布
| 语句类型 | 数量 |
|---------|------|
| TRANSACTION | 99,655 |
| SELECT | 51,982 |
| SET | 2,340 |
| INSERT | 2,256 |
| CREATE | 1,817 |
| DELETE | 441 |
### 4.4 典型业务SQL
崩溃前的主要负载为每分钟执行复杂 CTE 的 INSERT 语句:
```sql
INSERT INTO position_by_minute
WITH current_minute_changes AS (
SELECT instrument_id, MAX(produce_time) as max_time
FROM position
WHERE produce_time > 1786934880000000000
AND produce_time <= 1786934940000000000
AND expiry_date >= date '20260817'
GROUP BY instrument_id
),
current_records AS (
SELECT base.*
FROM position base
JOIN current_minute_changes cmc
ON base.instrument_id = cmc.instrument_id
AND base.produce_time = cmc.max_time
),
previous_snapshot_max_time AS (
SELECT prev.instrument_id, MAX(prev.produce_time) as prev_max_time
FROM position_by_minute prev
WHERE prev.snapshot_minute = '2026-08-17 10:48:00'
AND prev.zs_trading_date = 20260817
AND NOT EXISTS (
SELECT 1 FROM current_minute_changes cmc
WHERE prev.instrument_id = cmc.instrument_id
)
GROUP BY prev.instrument_id
),
...
SELECT '2026-08-17 10:49:00' as snapshot_minute, curr.*, 20260817 as
zs_trading_date
FROM current_records curr
UNION ALL
SELECT '2026-08-17 10:49:00' as snapshot_minute, prev.*, 20260817 as
zs_trading_date
FROM previous_base_records prev
```
查询特征:扫描 57M 行 / 62GB,峰值内存 752MB,CpuTimeMS 高达 329,538。
### 4.5 连接模式
- 客户端:MySQL Connector/J 8.0.33
- 来源:全部来自 172.24.16.108
- 模式:高频创建/断开连接(每分钟INSERT + 连接验证 + SET操作)
### 4.6 group_commit 问题
日志中还发现 group_commit 相关的事务冲突:
```
AnalysisException: begin transaction failed.
Label [group_commit_0a474c8412a331d3_cedb28f79ff3758e] has already been used,
relate to txn [3116755], status [PREPARE]
```
## 五、客户端正常close能否避免OOM
### 结论:不能完全避免
### 5.1 原因一:aliveSessionIds 的清理与连接关闭是解耦的
MAT 堆栈显示泄漏发生在 `FESessionMgr$FEAliveSessionHandler.run()` ->
`Env.getAllAliveSessionIds()`。这是一个**定期守护线程**,通过跨 FE 同步来清理过期 session
ID,**不是**在连接 close 时触发的。
即使客户端正常 close 连接,session ID 仍然留在 `aliveSessionIds` map 中,等待下次定期清理。而清理机制本身已经失效了。
### 5.2 原因二:清理机制从8月14日就已失效
```
8/14 RejectedExecutionException: queue size is full: all-fe-session-mgr-pool
(8次)
8/15 同上 (6次)
8/16 同上 (4次)
8/17 同上 (27次) <- 密集爆发
```
线程池队列满了,清理任务**被拒绝执行**。同时 FE 间同步也失败:
```
Error occurred when get alive session from 172.24.16.108, msg = Socket is
closed by peer
Error occurred when get alive session from 172.24.16.109, msg = Socket is
closed by peer
```
清理线程跑不起来 -> session ID 只增不减 -> 14天累积1.8亿条目 -> OOM
### 5.3 原因三:prod_qd_ab_wxh_w 是最大贡献者
该用户单日执行了近10万次 TRANSACTION + 2256次 INSERT,全部来自 172.24.16.108。按14天外推,产生了大量
session。
### 5.4 客户端正常close的效果
| 措施 | 效果 |
|------|------|
| 客户端正常close连接 | 减少session创建速率,**延缓**OOM发生 |
| 但无法阻止 | 清理机制失效后,即使close了,session ID仍在map中 |
| OOM触发点 | 与连接关闭无关,是定期清理任务自身在拷贝时OOM |
### 5.5 根本结论
这是 Doris 3.1.4 的**服务端Bug**,不是单纯的客户端问题:
1. **`FESessionMgr` 的 `aliveSessionIds` 缺少有效的过期清理机制** — 只依赖跨 FE 定期同步,一旦同步失败就只增不减
2. **`all-fe-session-mgr-pool` 线程池队列太小** — 高并发下队列满,清理任务被拒绝
3. **`Env.getAllAliveSessionIds()` 实现有缺陷** — 直接 `new ArrayList(keySet())`
全量拷贝,session 数量大时必然 OOM
**即使3个用户全部正常close,只要清理机制失效,session ID仍会累积到OOM。** 正常close只能减缓累积速度,不能根本解决问题。
我的问题是,目前Doris3.1.4 fe节点是否发现类似内存泄露反馈?
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]