JSP在CentOS上出现内存泄漏怎么办
小樊
38
2025-12-31 09:21:32
定位与修复步骤
- 确认是否为真实泄漏还是配置不足:先区分是“泄漏”还是“容量不够”。在 CentOS 上用 jstat -gc 观察 Full GC 后老年代是否持续无法回收、占用不断攀升;若是容量不足,应先做容量与参数调优,再继续查泄漏。JVM 常见默认堆为物理内存的 1/64(初始)与 1/4(最大),很多场景默认 128MB 堆明显偏小,容易误判为泄漏。用 jmap -dump:format=b,file=heap.bin 抓取堆转储,再用 Eclipse MAT 的 Dominator Tree、Leak Suspects、Histogram 定位大对象与根引用链。启动参数加上 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log 记录 GC 日志,观察 Full GC 频率与回收效果。必要时在测试环境复现,避免影响线上。
JSP与容器的常见根因与修复
- 类加载与热部署累积(PermGen/Metaspace 问题):大量第三方 JAR、频繁 JSP 预编译或反复热部署,会导致方法区/元空间持续增长。做法:将公共 JAR 放到 Tomcat/shared/lib 减少重复加载;合理设置非堆内存(JDK 7 用 -XX:PermSize / -XX:MaxPermSize;JDK 8+ 用 -XX:MetaspaceSize / -XX:MaxMetaspaceSize);减少热部署频率或改为蓝绿发布。
- Session 未合理失效:高并发下若 session 超时过长,会持续占用内存。建议:在不需要会话的页面使用 <%@ page session=“false” %>;在 web.xml 设置合理的 (单位分钟),并避免在 Session 中存放大对象或缓存未清理的数据。
- 资源未关闭(连接、结果集、流):在 JSP/Servlet 中务必在 try-finally 或 try-with-resources 中关闭 Connection、Statement、ResultSet、InputStream/OutputStream;避免在 JSP 中直接写数据库逻辑,推荐放到 DAO/Service。
- 静态集合与缓存泄漏:全局 static Map/List 或缓存无限增长、无过期策略;使用 WeakHashMap/软引用、设置容量上限与过期淘汰(如 Guava Cache/LRU),定期清理。
- ThreadLocal 使用不当:线程池场景下 ThreadLocal 不调用 remove() 会导致对象无法回收;在请求结束前务必清理,或使用 try-finally 确保释放。
Tomcat与JVM参数建议
- 堆与新生代:将 -Xms 与 -Xmx 设为相同以避免运行期扩缩容抖动,通常不超过可用物理内存的 80%;可按 -Xmn ≈ 1/4 × -Xmx 配置新生代。示例:-Xms4g -Xmx4g -Xmn1g。
- 元空间(JDK 8+):按需设置 -XX:MetaspaceSize 与 -XX:MaxMetaspaceSize,避免过小导致频繁 Full GC 或过大浪费内存。
- GC 日志与监控:开启 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log,配合 jstat/jmap/MAT 持续观测。
- 容器与部署:减少应用间 JAR 重复;控制热部署频率;必要时分离公共 JAR 到 shared/lib。
快速排查清单
- 用 jstat -gc 观察 FGC 后老年代是否回收;用 jmap -dump + MAT 找 Dominator Tree 与泄漏疑点;开启 GC 日志确认回收效率。
- 检查 Session:页面是否真的需要会话(不需要则 session=“false”);web.xml 的 session-timeout 是否合理;避免在 Session 中缓存大对象。
- 检查 JSP/Java 代码:是否有未关闭的 Connection/ResultSet/Stream;是否有 static 集合/缓存无限增长;ThreadLocal 是否在 finally 中 remove()。
- 检查 类加载:是否存在 JAR 重复、频繁热部署;公共 JAR 是否统一到 Tomcat/shared/lib;非堆/元空间是否配置合理。