代码之家  ›  专栏  ›  技术社区  ›  Peter Å tibraný

在Java中为局部变量设置空值是否有助于GC

  •  40
  • Peter Å tibraný  · 技术社区  · 17 年前

    myLocalVar = null; 语句放入finally子句,然后离开方法。原因是帮助GC。我被告知下次服务器崩溃时我会在晚上收到短信,所以我最好这样做:-)。

    我认为这是毫无意义的,因为myLocalVar的作用域是方法,并且一旦方法退出就会“丢失”。额外的空值只会污染代码,但在其他方面是无害的。

    要澄清,方法如下所示:

    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
      }
    }
    
    15 回复  |  直到 17 年前
        1
  •  31
  •   biziclop    8 年前

    有一份旧的Sun文档, Java Platform Performance (遗憾的是,现在链接断了,我还没有找到新的链接),它描述了一种情况,即取消掉了范围的局部变量实际上会对GC产生影响。

    然而,本文提到了一个非常古老的java版本。如中所述 this question

        2
  •  18
  •   SCdF    17 年前

    JavaGC应该是“健全的”,但不一定立即“完成”。换句话说,它的设计永远不会消除至少一条路径仍然可以访问的对象(从而导致悬空引用)。它不一定是立即完成的,因为它可能需要时间才能删除所有可以删除的内容。

    我认为大多数GC神话都来自对这个概念的误解。 很多人养太多了 实例变量 活着,这会引起问题,但这当然不是问题所在。

    其他人将局部变量放入实例变量中(例如,通过将其传递给函数),然后认为以某种方式取消局部变量会消除该变量,这当然是不正确的。

    最后,有些人过度依赖GC,认为它会为他们关闭功能(例如,当变量被移除时关闭连接),但事实并非如此。我认为这句话的来源是“我真的已经完成了,但我不确定如何确保这一点”。

    所以是的,你是对的,这是不必要的。

        3
  •  9
  •   Tim Frey    17 年前

        4
  •  8
  •   Matt Briggs    17 年前

    gc知道它在做什么。将var置零不会伤害任何东西,但也不会真正帮助任何东西。杰夫有一个 pretty funny post 就在前几天。

        5
  •  7
  •   David Schmitt    17 年前

    据我所知, 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();
    }
    
        6
  •  5
  •   Peter Å tibraný    17 年前

    在某些边缘情况下,将局部变量置零确实有帮助。这不适用于原始问题中的情况,但无论如何是教育性的。。。让我们来考虑一下这个项目:

    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."<init>":()V
    const #2 = class        #12;    //  Main
    const #3 = Method       #2.#11; //  Main."<init>":()V
    const #4 = class        #13;    //  java/lang/Object
    const #5 = Asciz        <init>;
    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;//  "<init>":()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."<init>":()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 "<init>":()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 on java-cz mailing list (抱歉,用捷克语)

        7
  •  3
  •   Eddie    17 年前

    Effective Java 2nd Edition ,作者建议不要对局部变量进行不必要的置零。请参阅有效Java,第二版,第6项:消除过时的对象引用,了解完整的编写过程。

    你也可以在文章中看到这一点 Creating and Destroying Java Objects 整篇文章 找到约书亚·布洛赫同意你的地方。

    当局部变量不在范围内时,它与对它的引用为null完全相同。

    编辑:在Sun网站上添加有效Java第二版的链接

        8
  •  2
  •   krosenvold    17 年前

    回到JDK1.3,我确实有一个非常大的对象图的例子,它在图中也包含很多循环引用。清除一些引用确实显著地提高了GC时间。

    我不确定这是否适用于现代虚拟机。垃圾收集器变得越来越聪明。

        9
  •  1
  •   Dave Costa    17 年前

    之前 方法的结尾。这将允许GC在方法完成之前收集该对象,这可能很有用。

        10
  •  1
  •   Michael Myers KitsuneYMG    17 年前

    只有两种情况下,我发现将变量设置为null很有用:

    • 在字段中创建大型对象的单元测试。单元测试仪可以在所有测试的生命周期内保留测试对象和您创建的对象。这可能会导致检测仪内存不足。在这种情况下,最好使用局部变量而不是字段,但是如果需要字段,可以在拆卸时清除该字段。
    • 循环引用可以由GC清理,但不能使用简单的增量集合。这可能意味着具有循环参照的对象需要更多的工作才能清除,并且比其他情况下寿命更长。一般来说,这并不重要,但如果您正试图减少全部GC时间,它可以帮助打破循环引用。
        11
  •  0
  •   falstro    17 年前

    我不知道技术细节,但据我记忆所及,变量只不过是来自当前堆栈帧的引用,在删除此引用之前,无法对对象进行垃圾收集。现在,但是显式地将其设置为null,您已经确保引用已消失。如果你不这样做,你基本上让VM决定何时清除这个引用,这可能是或者可能不是在离开范围时(与C++不同,如果对象位于堆栈上,必须被销毁)。可能是在堆栈帧被下一个帧覆盖时。我不确定是否真的有这样一个虚拟机。

    虽然答案很短,但这是不必要的,最终会被标记和扫描。这至多是个时间问题。

        12
  •  0
  •   Michael Myers KitsuneYMG    17 年前

    在某些情况下,空变量(通常是实例或类变量)可能很有用。但是取消一个 地方的 方法结束前的变量绝对不起任何作用。

    null

        13
  •  0
  •   Bill K    17 年前

    这几乎从来都不是问题,大多数情况下,为对象设置空值是没有用的。

    当您想到对象分配和释放时,请注意“系统”处理的事情:活动线程、未被dispose()d处理的窗口,以及一到两件我现在不记得的事情。

    系统中的每个对象都“挂起”在一棵倒立的巨树上的这些挂载点上。如果你从这些“根”上割下任何一根树枝,整个树枝就会掉到地上,并被垃圾收集的割草机收集起来。

    (顺便说一句,这些trim非常有效,甚至比C++的free更有效,因为它们不需要在释放对象时触摸每个对象)

        14
  •  0
  •   ReneS    17 年前

    如果您不再需要本地范围中的大型对象,那么可以给JVM一个提示并将引用设置为NULL。

    public void foobar()
    {
        List<SomeObject> dataList = new ArrayList<SomeObject>();
    
        // heavy computation here where objects are added to dataList 
        // and the list grows, maybe you will sort the list and
        // you just need the first element... 
    
        SomeObject smallest = dataList.get(0);
    
        // more heavy computation will follow, where dataList is never used again
        // so give the JVM a hint to drop it on its on discretion
        dataList = null;
    
        // ok, do your stuff other heavy stuff here... maybe you even need the 
        // memory that was occupied by dataList before... 
    
        // end of game and the JVM will take care of everything, no hints needed
    }
    

        15
  •  0
  •   Kai    17 年前

    对于GC来说,这样的局部变量的空值不仅没有意义,而且可能会导致不必要地将变量加载到寄存器中以将其空值,这会使情况变得更糟。想象一下,如果在最后一次读取或写入myLocalVar之间有1000行代码,然后引用它只是为了消除引用。该值早已从寄存器中消失,但您必须将其加载回内存才能使用它。