wangcool opened a new issue, #67199:
URL: https://github.com/apache/doris/issues/67199

   ### Search before asking
   
   - [x] I had searched in the 
[issues](https://github.com/apache/doris/issues?q=is%3Aissue) and found no 
similar issues.
   
   
   ### Version
   
   # 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节点是否发现类似内存泄露反馈?
   
   
   ### What's Wrong?
   
   我的问题是,目前Doris3.1.4 fe节点是否发现类似内存泄露反馈?
   
   ### What You Expected?
   
   确认Doris3.1.4 fe节点是否存在内存泄露问题
   
   ### How to Reproduce?
   
   _No response_
   
   ### Anything Else?
   
   _No response_
   
   ### Are you willing to submit PR?
   
   - [x] Yes I am willing to submit a PR!
   
   ### Code of Conduct
   
   - [x] I agree to follow this project's [Code of 
Conduct](https://www.apache.org/foundation/policies/conduct)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to