CentOS 上 WebLogic 内存泄漏的定位与解决
一、快速判断与应急
- 识别症状:在 GC 日志中出现越来越长的停顿、Full GC 频繁或回收无效;WebLogic 服务器日志出现 java.lang.OutOfMemoryError(如 PermGen/Metaspace 或 Java heap space);操作系统层面 top/free 显示 RES 持续增长不回落。
- 应急缓解:临时扩容堆(如将 -Xms 与 -Xmx 设为相同值,例如 -Xms4g -Xmx4g),重启受影响的 Managed Server;同时开启 -verbose:gc/-Xloggc 输出 GC 日志,便于后续分析。
- 重要认知:单纯增大堆或永久代只能延缓问题,必须结合 堆转储 与 根因修复 才能根治。
二、定位步骤
- 收集证据:
- 打开并保留 GC 日志;
- 在 WebLogic 控制台或通过 JMX 触发 Heap Dump(.hprof),必要时获取 线程转储 与 JVM 参数快照。
- 分析堆:使用 Eclipse MAT 或 VisualVM 打开 .hprof,查看 Dominator Tree、Histogram、可疑类的 GC Roots 最短路径,识别持续增长的对象集合与泄漏根因。
- 结合日志:对照 WebLogic 日志 与 GC 日志 的时间线,确认泄漏是否与 部署/取消部署、定时任务、流量高峰 等事件相关。
三、常见根因与修复要点
- JDBC/连接池泄漏:未正确关闭 Connection/Statement/ResultSet,或连接未归还到池;现象包括连接池占用随时间攀升、数据库端会话/游标耗尽。修复:在代码中用 try-finally/try-with-resources 确保关闭;校验 WebLogic JDBC 连接池 的 最大连接数、超时、回收策略 与数据库 最大连接/游标 匹配;必要时开启 Test Reserved/Released Connections(仅当数据库不稳定时)。
- 第三方驱动类加载器泄漏:以 MySQL 为例,老版本驱动会维护 ConnectionPhantomReference 集合,若连接未正确关闭会造成对象堆积与 Full GC 拉长。修复:升级到修复该问题的驱动版本;确保连接严格关闭;在复杂驱动场景下可考虑 G1 GC 改善停顿与回收效率。
- 热部署/类加载器泄漏:反复 redeploy 后 PermGen/Metaspace 增长,常见于未注销 ServletContextListener/Filter、线程/定时任务未停、静态引用未清理。修复:应用停止时清理 ThreadLocal、停止 Timer/Executor、注销 监听器;避免在应用作用域外持有类或对象引用;必要时调整 -XX:MaxMetaspaceSize 并配合代码整改。
- 缓存与集合膨胀:将大对象或海量数据缓存在 static Map/Cache 且无过期/淘汰策略,导致 Java heap space。修复:引入 TTL/LRU 策略(如 Guava Cache/Redis),限制最大容量,避免把大文件/大结果集长期驻留内存。
四、JVM 与 WebLogic 配置建议
- 堆与 GC:将 -Xms 与 -Xmx 设为相同(如 -Xms4g -Xmx4g),减少堆大小动态调整带来的停顿;根据负载与停顿目标选择并调优 GC 策略(如 G1 GC 适合大堆与低停顿诉求)。
- 元空间:若使用 JDK 8- 出现 PermGen 问题,适当增大 -XX:MaxPermSize;若使用 JDK 8+ 关注 Metaspace,通过 -XX:MaxMetaspaceSize 限制并观察 Metaspace 使用曲线。
- 启动参数位置:在 setDomainEnv.sh 中设置 JAVA_OPTS/MEM_ARGS(如 -Xms/-Xmx/-XX:+UseG1GC),变更后重启;生产环境建议通过 控制台 统一管理服务器级 JVM 参数。
- 连接池与线程:连接池最大值与 WebLogic 线程数 匹配,避免过大导致数据库资源紧张;合理设置 Statement Cache Size,防止数据库 游标 爆炸;在数据库不稳定时再启用 Test Reserved/Released Connections。
五、监控与预防
- 持续监控:在生产环境启用 GC 日志 与 Heap Dump 按需采集;使用 JConsole/VisualVM 或 WebLogic 自带监控,观察 堆使用、GC 次数/停顿、线程数、JDBC 连接数 的趋势。
- 容量与告警:为 堆使用率、Full GC 次数、连接池占用 设置阈值告警;当发现 线性/指数 增长趋势时优先排查泄漏而非扩容。
- 发布与回滚:变更/热部署前进行 基线采集(GC、内存、线程、连接),变更后对比差异;建立 快速回滚 与 演练 机制,缩短故障恢复时间。