问题描述:
生产环境运行一段时间后,节点服务均频繁发生full gc,最多的一天将近百次,full gc的时间也越来越长,有内存泄露的可能。
原因分析:
服务使用Mina的SftpFileSystem进行文件处理,其中引用的一个SftpClient对象使用了链式编程,对象使用后没有进行关闭,无法被回收,导致应用线程占用的内存越来越大,fullgc可回收的内存越来越少,最终会导致内存泄露,应用宕机。
Iterable<SftpClient.DirEntry> dirEntries = sftpFileSystem.getClient().listDir(handle)感觉还是编码习惯问题,该写法并没有逻辑问题,只是不容易发现需要关闭的对象。
该问题与github上apache mina-ssdh版本2.9.x及以下源码存在的一个SftpClient对象内存泄露问题相似,官方已在2.10.x及以上版本修复,2.9.x版本内存泄露源码如下:
堆栈信息:
解决方法:
1、非链式编程,SftpClient对象使用局部变量定义,这样就容易看出对象是否需要关闭
SftpClient sftpClient = sftpFileSystem.getClient();
sftpClient.listDir(handle)
2、在finally代码块中调用其close方法进行关闭
if (null != sftpClient) {sftpClient.close();
}
反思与总结:
1、手动或使用try-resources关闭资源型对象
使用Mina进行SFTP连接处理文件过程中,要及时对相关资源对象进行手动关闭或使用try-resources自动关闭,防止出现内存泄露。对于此次问题,常用但不限于以下对象:
1、SftpFileSystem
2、ClientSession
3、SshClient
4、SftpClient2、资源性对象,尽量不要使用链式编程
在开发过程中,有时为了编码方便,对于复合性对象属性的获取,大多会使用链式编程,但对于资源对象,如果使用链式编程,可能会忘记对象的关闭,导致资源对象无法回收,从而导致内存泄露。
链式编程(不推荐):sftpFileSystem.getClient().listDir(handle) 非链式编程(推荐):SftpClient sftpClient = sftpFileSystem.getClient(); sftpClient.listDir(handle);
详情请参考Github: