【问题标题】:Will Java's garbage collector go ahead and take care of variables declared within loops?Java 的垃圾收集器会继续处理循环中声明的变量吗?
【发布时间】:2010-10-18 15:12:40
【问题描述】:

如果我有:

for (int i; i != 100; i++) {
    ArrayList<String> myList = buildList();
    //... more work here
}

我是否必须在循环结束时将 myList 设置为 null 才能让 GC 回收它用于 myList 的内存?

【问题讨论】:

    标签: java garbage-collection


    【解决方案1】:

    GC 会自动清理所有不再在作用域内的变量。

    在块中声明的变量,例如 for 循环,将只在该块的范围内。代码退出块后,GC 将删除它。一旦循环的迭代结束,就会发生这种情况,因此,只要循环的每次迭代结束,列表就可以进行垃圾回收。

    变量的范围也是为什么i 在示例循环之后无效的原因。

    请注意,只有在循环中使用变量时才会出现这种情况。如果你将它传递给另一个保留对它的引用的方法,你的变量将不会被垃圾回收。

    【讨论】:

    • 我认为他更担心在循环的先前迭代中分配给myList 的对象将被GC'd,即使仍在循环中。不是在循环之后。
    • 不知道这个回答够不够完整。首先,整数和其他值类型在堆栈上分配,与垃圾回收无关。其次,myList 可能有也可能没有资格进行垃圾收集;这取决于其他引用是否存储在代码中的其他位置。
    • @Outlaw Programmer:“整数和其他值类型在堆栈上分配”。您对此声明有参考吗?我相信 JVM 可以选择是使用堆分配还是堆栈分配,并且会根据各种启发式方法来选择。还是您在谈论原始类型?
    • 堆栈上的变量确实不会被垃圾回收,我只是指变量范围。我编辑了答案以提及其他参考资料,这是我错过的一个很好的观点。
    • -1 GC 不清理变量。而且GC和i无关,那是一个原始的栈变量,不是堆上的实例。
    【解决方案2】:

    主啊,不! Java 的 GC 比这要智能得多,muchmuch

    【讨论】:

    • 好吧,如果buildList() 的结果覆盖finalize(),那将是灾难性的。但让我们希望代码是理智的。
    • 对象覆盖 finalize() 得到垃圾收集器的特殊处理。
    • 当然可以,但我想知道它是如何导致“灾难性”后果的。
    • 因为覆盖 finalize 的对象是与负责跟踪终结和东西的“终结器对象”一起创建的。这提高了 gen 0 集合的存活率,并将“终结器对象”推送到更高代,使它们更难收集。看看这里的例子:elliottback.com/wp/java-memory-leaks-w-finalize-examples
    • @Martinho:我看不到那篇文章中的任何内容如何处理在循环中使用 finalize 方法的危险,而是对finalize() 的愚蠢实现的一般警告,无论是否在循环中.那么简单地覆盖finalize() 是如何导致灾难性结果的呢?
    【解决方案3】:

    GC 会在需要时回收所有无法访问的实例。它没有 GC 变量。

    变量imyList 存在于堆栈中。当 for 循环结束时(当它们超出范围时),它们将被压出堆栈,因此它们的内存被回收。然后变量将消失。在引用变量 myList 消失之前将其设置为 null 确实没有什么区别(对于 GC 实例)。如果 GC 也会为引用的实例 myList 回收内存,这实际上取决于您是否有另一个对同一实例的引用。

    变量不会被 GC,实例会。

    【讨论】:

      【解决方案4】:

      这可能有助于理解 for 循环背后的语法。这在JLS Section 14.14.1 中进行了讨论。

      BasicForStatement:
          for ( ForInitopt ; Expressionopt ; ForUpdateopt ) Statement
      
      ...
      
      Statement:
          StatementWithoutTrailingSubstatement
          ...
      
      StatementWithoutTrailingSubstatement:
          Block
          EmptyStatement
          ExpressionStatement
          ...
      
      
      Block:
          { BlockStatementsopt }
      
      BlockStatements:
          BlockStatement
          BlockStatements BlockStatement
      
      BlockStatement:
          LocalVariableDeclarationStatement
          ClassDeclaration
          Statement
      

      语句,一如既往,可以是单个语句或块(用花括号括起来的语句)。一个块代表一个新的词法范围,其中声明的变量仅对该块是本地的。许多人没有意识到这并不是 for 循环所独有的。我可以在方法中的任何位置放置块语句:

       public void someMethod() {
      
           {
               List<String> myList = new ArrayList<String>();
               System.out.println(myList);
           }
           System.out.println(myList.size()); //compile error: myList out of scope
       }
      

      无论如何,这并不符合我的预期。可以说它与它是一个循环的事实关系不大,而与它是一个块的事实有关(如果你选择不使用块语句,你不能声明新的局部变量,所以问题是无关)。

      【讨论】:

      • 这是否意味着 GC 不用于管理块内声明的变量/实例的内存?块结束后,变量/实例会自动回收吗?
      • 不,不是这个意思。实例仍然放在堆上并由 gc 回收。请记住,该块很容易泄漏对这些实例之一的引用。我想说的是,变量的范围仅限于一个块,无论使用什么控制结构。
      【解决方案5】:

      您不必为垃圾烦恼。 Java GC 会自动执行此操作。 但我的建议是,作为一名优秀的开发人员,你必须进行实践 用于释放垃圾。 因为 GC 需要一些处理时间。 因此,通过照顾好自己,我们肯定会提高性能。

      【讨论】:

        猜你喜欢
        • 2013-05-11
        • 2012-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-12
        相关资源
        最近更新 更多