一、Tomcat日志中内存泄漏的常见迹象
在Tomcat日志(如catalina.out、localhost.log或访问日志)中,内存泄漏通常会表现为以下特征,是初步判断的依据:
Full GC记录(可通过-XX:+PrintGCDetails参数开启详细GC日志),且频率随时间逐渐增加,说明JVM频繁尝试回收内存但仍无法满足需求。jstat -gcutil <pid>命令或日志中的内存监控信息,发现堆内存(尤其是老年代)使用量随时间持续上升,无回落趋势。ThreadLocal相关警告(如java.lang.OutOfMemoryError: Java heap space伴随ThreadLocalMap条目过多),说明ThreadLocal变量未及时清理,导致线程池中的对象无法回收。二、通过日志及工具发现内存泄漏的具体方法
通过添加JVM参数开启详细GC日志,记录垃圾回收的类别、时间、回收前后的内存变化等信息,帮助识别内存泄漏趋势:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log
分析时重点关注:
当怀疑存在内存泄漏时,通过jmap命令生成堆转储文件(Heap Dump),包含Java堆中所有对象的快照,用于后续分析:
jmap -dump:format=b,file=heapdump.hprof <tomcat_pid>
注意:<tomcat_pid>可通过jps -l命令获取Tomcat进程ID。
生成后,使用Eclipse MAT(Memory Analyzer Tool)或VisualVM等工具打开堆转储文件,分析:
byte[]、HashMap等);ThreadLocalMap中大量未清理的条目。Tomcat提供了LeakDetectionListener监听器,可主动检测资源(如ServletContext、数据库连接池、线程池等)的泄漏,并在日志中输出警告信息。
在META-INF/context.xml(应用级)或conf/context.xml(全局)中添加以下配置:
<Context>
<Listener className="org.apache.catalina.core.LeakDetectionListener"
threshold="60000" /> <!-- threshold单位为毫秒,超过60秒未释放的资源会触发警告 -->
</Context>
查看catalina.out日志,若出现类似SEVERE: The web application [myapp] appears to have started a thread named [pool-1-thread-1] but has failed to stop it的警告,说明存在资源泄漏。
通过JMX(Java Management Extensions)工具(如jconsole、VisualVM)连接到Tomcat实例,实时监控以下指标:
-Xmx(最大堆内存)上限;内存泄漏的常见原因是资源未正确关闭,如数据库连接、文件流、网络连接等。通过日志中的异常(如java.sql.SQLException: Already closed)或堆转储分析,定位未关闭的资源。
修复建议:
try-with-resources语句(Java 7+),自动关闭资源:try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users");
ResultSet rs = stmt.executeQuery()) {
// 处理结果集
} catch (SQLException e) {
e.printStackTrace();
}
finally块中关闭,并捕获关闭异常:Connection conn = null;
try {
conn = dataSource.getConnection();
// 使用连接
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// 记录日志,避免吞掉异常
logger.error("Failed to close connection", e);
}
}
}
通过以上方法,可结合Tomcat日志与工具分析,逐步定位并解决内存泄漏问题。需注意的是,内存泄漏的排查是一个迭代过程,可能需要多次分析GC日志、堆转储和代码,才能彻底解决问题。