JVM内存泄漏的原因
所谓JVM内存泄漏,就是在JVM做GC操作时,我们认为本来应该”没有用”的对象,没有被JVM回收掉,其主要原因是因为从JVM的GC ROOT
出发,存在一条路径可以寻找到该对象。
常见的GC ROOT
主要有以下几种:
- System Class - 由系统类加载器(system class loader)加载的对象
- Thread - 运行中的线程对象(经常导致内存泄漏的原因,由于Thread对象中持有一个加载该线程类的ClassLoader对象,如果该线程不终止–>ClassLoader对象无法释放–>方法区中该ClassLoader加载的类无法释放–>最终有可能导致方法区内存溢出)
- Busy Monitor - 用于监控的对象
- Native Stack - 本地方法栈引用的对象?
1. MySQL驱动
如果应用中使用了MySQL 5.1.24+的JDBC驱动的话,在不停止应用server,重新部署应用的时候,会发现JVM的Pergem区有很多内存不能回收,通过VisualVM等工具,可以看到有一个名为Abandoned connection cleanup thread
的线程还在后台跑,导致应用的ClassLoader无法释放。
解决方法1: 创建一个类似以下代码的Listener
|
|
然后在web.xml
里配置该Listener
缺点: 该方法需要在应用中引入jdbc的驱动包,不太优雅,而且由于JDBC驱动的注册是JVM级别的,如果该server上有其他应用也使用MySQL的话,有可能产生其他问题。
解决方法2: 由应用服务器负责加载MySQL的驱动
将MySQL的jdbc驱动包放到应用服务器的classpath,例如tomcat中的lib
目录,由应用服务器的ClassLoader来加载,并启动Abandoned connection cleanup thread
线程,这样应用的ClassLoader就无需对MySQL的类进行装载和卸载。
2. commons-pool
使用commons-pool
组件,如果timeBetweenEvictionRunsMillis
属性的值设为>0
的值,则会在后台启动一个名为common-pool-EvictionTimer
的线程,如果在应用卸载时(JVM没有重启),没有调用pool的close方法,则该线程不会停止,导致应用ClassLoader无法回收,从而导致方法区内存泄漏。