今天看到 一篇文章
,讨论 【不使用的对象应手动赋值为null】
这件事。
文中给了这样一个例子:
public static void main(String[] args) { if (true) { byte[] placeHolder = new byte[64 * 1024 * 1024]; System.out.println(placeHolder.length / 1024); } System.gc(); } 执行结果: 65536 [GC 68239K->65952K(125952K), 0.0014820 secs] [Full GC 65952K->65881K(125952K), 0.0093860 secs]
上面这段代码执行gc后内存占用没有降下来,没有把placeHolder回收掉。
而下面这段代码就会被回收:
public static void main(String[] args) { if (true) { byte[] placeHolder = new byte[64 * 1024 * 1024]; System.out.println(placeHolder.length / 1024); placeHolder = null; } System.gc(); } 执行结果: 65536 [GC 68239K->65952K(125952K), 0.0014910 secs] [Full GC 65952K->345K(125952K), 0.0099610 secs]
文章认为原因是 触发GC时,main()方法的运行时栈中,还存在有对args和placeHolder的引用,GC判断这两个对象都是存活的,不进行回收。
事实真的是这样吗?
我们不妨看看第一段代码的字节码大家就明白了。
0: ldc #2 // int 67108864 2: newarray byte 4: astore_1 5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 8: aload_1 9: arraylength 10: sipush 1024 13: idiv 14: invokevirtual #4 // Method java/io/PrintStream.println:(I)V 17: invokestatic #5 // Method java/lang/System.gc:()V 20: return
从字节码可以看到, if(true)
在编译时被优化掉了,代码相当于变成了下面这样,placeHolder的作用域变大了,所有在GC时不会被回收。
public static void main(String[] args) { byte[] placeHolder = new byte[64 * 1024 * 1024]; System.out.println(placeHolder.length / 1024); System.gc(); }
现在我们来换个写法,不让if条件被优化掉:
public static void main(String[] args) { if (args.length == 0) { byte[] placeHolder = new byte[64 * 1024 * 1024]; System.out.println(placeHolder.length / 1024); } System.gc(); } 执行结果:【省略了部分无关的GC输出】 65536 [GC (System.gc()) 70793K->66152K(251392K), 0.0008739 secs] [Full GC (System.gc()) 66152K->458K(251392K),0.0041778 secs]
可以看到,GC后placeHolder被回收了。
关于赋不赋值为null这件事,看个人编码习惯,我一般不喜欢做这些多余的操作,实际对GC也不会产生什么影响。那篇文章后面关于局部变量表重用的讨论是没问题的,但关于赋值为null因为使用了不妥当的例子而得到了错误的结论。