DB.close() 后 Oracle 仍有活动会话,因 close() 仅归还连接池或标记可复用,并非物理断开;需配置 Tomcat 连接池回收策略、启用 forceConnectionClose=true 参数、并查 V$LOCK 中 TX 锁而非仅 V$LOCKED_OBJECT。
为什么 DB.close() 后 Oracle 还有活动会话
调用 connection.close() 并不等于连接立刻从 oracle 服务端断开——它只是把连接归还给连接池,或标记为“可复用”。如果连接池没配置回收策略、或应用代码没真正触发物理关闭(比如用的是 tomcat jndi datasource 但没设 removeabandonedonborrow),那个会话在 oracle 的 v$session 里就会持续显示为 active 或 inactive,变成“僵尸连接”。
Tomcat + Oracle 场景下最常漏掉的三个配置项
你在 conf/context.xml 里只写了基础参数,缺了关键的清理开关:
-
removeAbandonedOnBorrow="true"(Tomcat 8 及以前)或removeAbandonedOnUsageExceeded="true"(Tomcat 9+),配合removeAbandonedTimeout="60",否则连接借出后长期没归还,池不会主动回收 -
testOnBorrow="true"+validationQuery="SELECT 1 FROM DUAL",否则借出前不验证连接有效性,可能把已断开的连接又发给业务线程 -
minIdle="0"(尤其非高峰期),避免空闲连接长期占着V$SESSION不放
forceConnectionClose=true 是 JDBC 层的“断连扳机”
Oracle JDBC 驱动默认行为是:close() 只释放客户端资源,不发 FIN 包给数据库。结果就是 Oracle 端还维持着 TCP 连接和事务上下文。必须在 JDBC URL 中显式加上:
jdbc:oracle:thin:@//192.168.1.248:1521/PRD?oracle.jdbc.thin.forceConnectionClose=true
这个参数仅对 Oracle 12c+ 有效;低于该版本需改用 oracle.net.CONNECT_TIMEOUT 和 oracle.net.READ_TIMEOUT 缩短等待窗口,再配合 SQLNET.EXPIRE_TIME 加速服务端探测。
别只查 V$LOCKED_OBJECT,TX 锁才是真凶
看到 V$LOCKED_OBJECT 为空就以为没锁?错。它只记录 DML 涉及的**对象级锁**,而 Java 异常中断后残留的大多是 TX(事务锁),得查:
立即学习“Java免费学习笔记(深入)”;
SELECT * FROM V$LOCK WHERE TYPE = 'TX' AND SID IN (SELECT SID FROM V$SESSION WHERE STATUS = 'INACTIVE');
这类锁不会自动释放,直到 PMON 触发清理(默认最长 120 秒)。所以问题本质不是“连接没关”,而是“事务没回滚 + 连接没通知服务端断开”。
真正的僵死进程往往卡在 TCP 层与 Oracle 事务状态的双重延迟里,不是加个 close() 就能解决的。


















