[JVM]请问a = null,对于垃圾回收,有效果吗
如A a = new A();
.........
最后用完了 a= null;
随便说个,随便听听……
各个JVM的实现方式不同,设null有没有影响也是不一定的。也就是说如果想确定一用完某个对象马上就释放其占用的内存空间,没有在各JVM上都通用的办法。
于是就可以看些有趣的例子了。许多JVM的实现都会在合适的时候把字节码即时编译为机器码,在这过程中有可能做方法内联、无用代码消除等诸多优化。看这个假象例子:
[code="java"]public class Foo {
public void bar() {
A a = new A();
a.doTask(this);
}
}
public class Quux {
public void baz() {
Foo foo = getFoo();
for (int i = 0; i < 100000; i++) {
foo.bar();
}
doSomeLongTask();
}
private void doSomeLongTask() {
// ...
}
}[/code]
Foo.bar()是一个小方法,而且在一个高强度的循环里被调用,则如果Quux.bar()被多调用几次,在某些JVM上就有机会看到实际运行的Quux.baz()代码变成了类似这样:
[code="java"]public void baz() {
Foo foo = getFoo();
if (Foo.class == foo.getClass()) { // guarded inline method cache
for (int i = 0; i < 100000; i++) {
A a = new A();
a.doTask(foo);
}
} else {
for (int i = 0; i < 100000; i++) {
foo.bar(); // fallback
}
}
doSomeLongTask();
}[/code]
甚至有可能进一步把A.doTask()也内联进来。
这样,a这个引用的作用域就跑到Quux.baz()里了。
如果原先在Foo.bar()里写上了a = null;会怎样呢?
1、这句有可能原本就被优化掉了(因为这是对变量的赋值,而赋值后没有代码再读取过这个值)。反正方法调用结束后局部变量自然消失,设不设null都一样 << 前提是没内联优化。
2、这句没有一开始就被优化掉,而是被内联来到了Quux.baz()里:
[code="java"]public void baz() {
Foo foo = getFoo();
if (Foo.class == foo.getClass()) { // guarded inline method cache
for (int i = 0; i < 100000; i++) {
A a = new A();
a.doTask(foo);
a = null;
}
} else {
for (int i = 0; i < 100000; i++) {
foo.bar(); // fallback
}
}
doSomeLongTask();
}[/code]
看起来似乎很有用对吧?似乎在Quux.baz()的中间就把a设为了null,减少了一个对new A()得到的对象的引用?
如果JVM有能力把代码内联进来,也就不差在计算变量的存活范围和消除无用代码了。于是a = null;照样被当作无用代码消除掉,同时也可以得知a变量的存货范围只有for循环里的两行,所以Quux.baz()的其它局部变量可以在其它时间占用a所在的寄存器或栈空间。设不设null还是一样。如果在执行doSomeLongTask()时进行GC,即时不设置a为null,GC也会认为没有那个引用,不影响可能的回收。
但如果没那么多优化,JVM就老老实实的执行没优化过的字节码呢?
[code="java"]public void baz() {
Foo foo = getFoo();
A a = new A();
a.doTask(foo);
a = null;
doSomeLongTask();
}[/code]
JVM可能会老老实实把全部局部变量都放在栈帧上,可能不消除无用代码,可能在doSomeLongTask()的执行过程中进行了GC。如果a没有被手工设为null,则GC的时候会看到栈上还存在一个指向new A()得到的对象的引用,于是认为该对象还是活的。
手工设置null最有用的场景恐怕就是在特定JVM上配合System.gc()来用了吧 =v=
这样是一种良好到编程习惯,
如果你有jprofile到话,你=null后,然后调用 System.gc() 看看效果,你就知道了
有效果,a = null 后,就去掉了一个指向 new A() 出来的那个对象的引用。
没有被任何符号引用或间接的对象,就会进入收垃圾的列表里,gc 有空就会把它收拾掉。如果想早点见到效果,可以调用 System.gc()。注意 gc 不受你管,所以 System.gc() 只是建议,不是命令。
要注意的是:这样不一定去掉了所有引用。
譬如 a = new A() 后, b = a, a = null 不能使这个对象进入 gc 列表。
对重复创建千百万次的对象,可以这么做增加点保障,但是我觉得形成习惯就不好了。
一来不一定解决漏内存问题,二来影响可读性 …… 如果还要程序员自己管理内存,挨个设 null,和 C++ 还有什么区别 ?
啊,打错字……
假象=>假想
存货=>存活 OTL
这样是一种好的编程习惯,对于垃圾回收有部分效果,不是绝对的,
和你使用的引用类型有关系的,看看这篇文章对你应该有帮助:
[url=http://www.iteye.com/topic/401478]理解 Java 的 GC 与 幽灵引用[/url]