22

又见【不使用的对象应手动赋值为null】 | 变猴旅程 | 从人变猴的历程

 4 years ago
source link: https://blog.darkness463.top/2019/11/19/objects-set-null/?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

又见【不使用的对象应手动赋值为null】

2019-11-19

513

今天看到一篇文章,讨论【不使用的对象应手动赋值为null】这件事。

文中给了这样一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
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回收掉。

而下面这段代码就会被回收:

1
2
3
4
5
6
7
8
9
10
11
12
13
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判断这两个对象都是存活的,不进行回收。

事实真的是这样吗?
我们不妨看看第一段代码的字节码大家就明白了。

1
2
3
4
5
6
7
8
9
10
11
 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时不会被回收。

1
2
3
4
5
public static void main(String[] args) {
byte[] placeHolder = new byte[64 * 1024 * 1024];
System.out.println(placeHolder.length / 1024);
System.gc();
}

现在我们来换个写法,不让if条件被优化掉:

1
2
3
4
5
6
7
8
9
10
11
12
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因为使用了不妥当的例子而得到了错误的结论。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK