背景
众所周知,在Java中,成员方法内可以使用this来引用当前对象,使用起来特别方便。但是在JVM中方法是在方法区中,所有的类的对象都共用了一个方法区,那么JVM是怎么知道this是指向哪个对象的呢?
其实为了实现这一功能,Java的处理方式很简单,在编译时,为每个成员方法都默默的添加了一个参数this,当调用这个方法时,把当前对象以参数的形式传进去即可。
本文重点不在this是怎么来的,因此简单验证下:
一个简单的java类:
编译后,使用javap来查看class文件
看Test方法和hi方法的args_size=1,源码中是0参数的,因此可以验证确实是偷偷的添加了一个参数,当然这里肯定是this参数了。
Memory Leak
回归正题,上面验证了成员方法中会默默的添加一个this参数,而参数其实是放在方法局部变量表中的。因此如果参数没有被明确赋值为null的话,那么这个参数就一直是以强引用的形态指向了该对象。在一般情况下,方法和对象的生命周期是一样的,也就是对象不存在了,方法也不会被调用,不会存在泄露。但是有些情况下,比如一些异步的操作,导致对象本来应该被回收掉时,方法还在被调用,因此这期间就会存在短暂的泄露,当然如果是耗时的方法,泄露会表现的比较明显。
下面是在有异步任务的情况下的泄露测试。
当testLeak为true时,打印结果为
当testLeak为false时,打印结果为
从打印结果可以看出,在测试泄露时,必须等到耗时方法执行结束后,该对象才能被gc,改进后的测试,在耗时方法未执行结束前,对象就已经可以被gc了。
解决方案
- 为了解决隐藏的this参数这个问题,可以从根源上尽可能去除方法中this局部变量的存在,比如将方法改成static的。
- 对于局部变量,比如弱引用的调用时,不要用局部变量保存从弱引用中get出来的值等。