Object.wait() 在 jstack 中通常表示线程在等待连接池空闲连接,而非等待锁;关键看其上层调用点(如 HikariPool.getConnection),结合 connection-timeout 和 maximum-pool-size 配置及 DB 连接上限综合判断是否池耗尽或存在连接泄漏。

看懂 jstack 里 Object.wait() 线程状态的真实含义
线程显示为 java.lang.Object.wait() 并不等于“在等锁”,它大概率是被池化资源(如数据库连接)阻塞了。比如 HikariCP 的 getConnection() 默认会调用 wait() 等待空闲连接;Druid 也类似,底层用 Condition.await(),jstack 中常归为 Object.wait()。关键不是“谁在 wait”,而是“wait 在哪个对象上”——重点看堆栈里 at java.lang.Object.wait(Native Method) 上一行的调用点。
快速定位是否为连接池耗尽:三步过滤法
拿到 jstack 全量输出后,按以下顺序 grep,比人工扫屏快得多:
-
grep -A 5 -B 5 "Object\.wait" thread_dump.log | grep -E "(Hikari|Druid|getConnection|borrowObject)"—— 看 wait 是否出现在连接获取路径中 -
grep "pool.*active" thread_dump.log | head -5—— 查看连接池监控日志(如有),或直接查应用日志里类似HikariPool-1 - Active connections: 20 (max=20)的语句 -
grep "TIMED_WAITING" thread_dump.log | wc -l和grep "WAITING" thread_dump.log | wc -l对比数量:若绝大多数线程卡在WAITING且都停在 getConnection,基本可断定池已满
HikariCP 下 Object.wait() 的典型堆栈与参数含义
常见堆栈形如:
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:196)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128)
这里 HikariPool.java:196 实际调用的是 addBagItem(waiters.get()) 后的 wait(),本质是等待新连接创建完成或旧连接归还。注意两个关键配置影响此行为:
-
connection-timeout(默认 30s):线程最多 wait 多久,超时抛SQLTimeoutException;若线上大量线程卡满 30s 后报错,说明池长期无连接可借 -
maximum-pool-size:必须结合 DB 侧最大连接数(如 MySQL 的max_connections)校验,不能只看应用配置 -
idle-timeout和max-lifetime设置过短,会导致连接频繁销毁重建,加剧 borrow 压力,间接引发 wait 堆积
为什么加机器或扩 maximum-pool-size 可能让问题更糟
盲目扩容容易忽略两个隐性瓶颈:
- DB 侧连接数硬上限:MySQL 默认
max_connections=151,如果单机配了maximum-pool-size=30,4 台机器就打满了,再加机器只会让所有节点一起卡在Object.wait() - 连接建立开销:每次新建物理连接要 TCP 握手 + SSL(如启用)+ 认证 + 初始化 session 变量,高并发下这个过程本身就会排队,表现为大量线程在
getConnection()前的newConnection()阶段阻塞,jstack 中可能显示为java.net.SocketInputStream.socketRead0,但最终仍会落入 wait 队列 - 连接泄漏未释放:最隐蔽的情况——某个 SQL 执行完没 close
ResultSet/Statement,导致连接无法归还,leak-detection-threshold(HikariCP)若未开启或设得过大,就会静默吃掉连接,直到池空
真正要盯住的,是连接池的「归还率」和「活跃时间分布」,而不是单纯看 wait 线程数。一个连接被借出 5 分钟不还,比 100 个线程各 wait 1 秒更危险。











