myLocalVar = null;
在离开方法之前,我被"强制"在finally子句中添加语句.原因是帮助GC.有人告诉我,下次服务器崩溃时我会收到短信,所以我最好这样做:-).
我认为这是毫无意义的,因为myLocalVar的范围是方法,并且一旦方法退出就会"丢失".额外的归零只会污染代码,否则无害.
我的问题是,这个关于帮助GC的神话来自哪里?(我被称为"Java记忆书")你知道"权威"的任何文章更深入地解释它吗?有可能这不是一个神话,但真的有所帮助吗?如果是这样,怎么样?可能会使局部变量归零会造成任何伤害?
为了澄清,方法看起来像这样:
void method() { MyClass myLocalVar = null; try { myLocalVar = get reference to object; ... do more here ... } finally { if (myLocalVar != null) { myLocalVar.close(); // it is resource which we should close } myLocalVar = null; // THIS IS THE LINE I AM TALKING ABOUT } }
Bill Michell.. 31
有一个旧的Sun文档,Java平台性能(链接很遗憾,现在已经坏了,我找不到新的),它描述了一种情况,即将一个超出范围的局部变量归零实际上会产生影响在GC上.
但是,本文提到了一个非常古老的java版本.正如本问题中所提到的(其中还包含了本文中描述的问题的原理),这不再影响当前的JVM实现.
有一个旧的Sun文档,Java平台性能(链接很遗憾,现在已经坏了,我找不到新的),它描述了一种情况,即将一个超出范围的局部变量归零实际上会产生影响在GC上.
但是,本文提到了一个非常古老的java版本.正如本问题中所提到的(其中还包含了本文中描述的问题的原理),这不再影响当前的JVM实现.
Java GC应该是"声音"但不一定立即"完整".换句话说,它被设计成永远不会消除至少一条路径仍可访问的对象(从而导致悬空引用).它不一定立即完成,因为它可能需要一些时间才能删除所有可以删除的内容.
我认为大多数GC神话来自对这个概念的误解.许多人保留了太多的实例变量,这会导致问题,但这当然不是问题.
其他人将局部变量放在一个实例变量中(例如,通过将其传递给函数),然后认为使局部变量无效以某种方式消除变量,这当然是不真实的.
最后,有些人过分关注GC并认为它会对它们进行功能关闭(例如,当删除变量时关闭连接),当然不是这种情况.我认为这一行的来源是"我真的真的用它,但我不知道如何确保".
所以,是的,你是正确的,这是不必要的.
不是在这种情况下.一旦函数返回,myLocalVar就会超出范围,因此将引用设置为null绝对不会.
这是一个神话,可以追溯到java首次出现时,C++家伙不信任gc.
gc知道它在做什么.归零var不会伤害任何东西,但它也不会真正帮助任何东西.就在前几天,杰夫有一个非常有趣的帖子.
据我所知,null
在它离开作用域之前立即变量对垃圾收集器没有任何影响.
当然,有些情况确实有所帮助.例如,何时var
不是局部变量而是成员或静态成员.然后销毁引用可能会使对象无法访问,因此有资格进行收集.
如果一个函数分配了很多临时内存来初始化一些数据以便进一步处理,并且在开始处理之前可以丢弃对临时内存的所有引用,那么它甚至可能对局部变量有帮助的情况:
SomeResult SomeFunction(SomeClass param) { TempData big = new TempData(param); IntermediateResult intermediate = big.GetIntermediateResult(); big = null; // allow GC to reclaim the memory before returning from the function intermediate.FurtherProcessing(); return intermediate.EvenMoreProcessing(); }
排除局部变量确实可以在某些情况下提供帮助。这不适用于原始问题中的情况,但是仍然具有教育意义。让我们考虑一下该程序:
public class Main { public static void main(String[] args) { { Main local = new Main(); // inner = null; } while (true) { // long running method } } }
如果inner = null;
将其注释掉,则local
在while循环期间无法对变量中的对象进行垃圾回收。原因是Java虚拟机不了解这样的范围。它所具有的只是:
D:\workspaces\workspace-3.4\test\src>javap -verbose -c Main public class Main extends java.lang.Object minor version: 0 major version: 50 Constant pool: const #1 = Method #4.#11; // java/lang/Object."":()V const #2 = class #12; // Main const #3 = Method #2.#11; // Main." ":()V const #4 = class #13; // java/lang/Object const #5 = Asciz ; const #6 = Asciz ()V; const #7 = Asciz Code; const #8 = Asciz main; const #9 = Asciz ([Ljava/lang/String;)V; const #10 = Asciz StackMapTable; const #11 = NameAndType #5:#6;// " ":()V const #12 = Asciz Main; const #13 = Asciz java/lang/Object; { public Main(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object." ":()V 4: return public static void main(java.lang.String[]); Code: Stack=2, Locals=2, Args_size=1 0: new #2; //class Main 3: dup 4: invokespecial #3; //Method " ":()V 7: astore_1 8: goto 8 StackMapTable: number_of_entries = 1 frame_type = 8 /* same */ }
没有有关局部变量范围的信息。因此,从JVM的角度来看,上述程序等效于:
public class Main { public Main() { } public static void main(String args[]) { Main main1 = new Main(); do ; while(true); } }
(由JAD反编译器生成)
结论:在这样的非常特殊的情况下,使局部变量为空有一些理由。但是,如果方法即将完成(例如在我最初的问题中),则无济于事。
这是受Zdenek Tronicek在java-cz邮件列表上的评论启发的(捷克语,很抱歉)