【问题标题】:Java - Can objects which are executing methods be garbage-collected?Java - 可以对正在执行方法的对象进行垃圾收集吗?
【发布时间】:2012-12-16 04:59:31
【问题描述】:

在 Java 中,我不假思索地做了以下事情:

public class Main {

    public void run() {
        // ...
    }

    public static void main(String[] args) {
        new Main().run();
    }
}

但是,最近我开始不确定这样做是否安全。毕竟,Main 对象在创建后就没有引用了(嗯,有 this 引用,但这算不算?),所以看起来垃圾收集器可能会在删除对象时删除该对象。它正在执行某事。所以也许main 方法应该是这样的:

    public static void main(String[] args) {
        Main m = new Main();
        m.run();
    }

现在,我很确定第一个版本可以正常工作,而且我从来没有遇到过任何问题,但我想知道在所有情况下这样做是否安全(不仅在特定的 JVM 中,而且最好根据语言规范对此类情况的规定)。

【问题讨论】:

标签: java object garbage-collection main


【解决方案1】:

如果正在执行对象方法,则表示有人拥有该引用。所以不,在执行方法时不能对对象进行 GC。

【讨论】:

  • +1 保留的对象是那些可以从线程根访问的对象,因此它们不能被定义为 GC。
  • 嗯,我怀疑是这样的,但我遇到的问题是在任何时候都没有明确的引用(即变量)指向创建的对象。这是否意味着,就GC而言,只要在语句中使用了对象(例如使用.运算符调用对象上的方法),这都算作隐式持有对该对象的引用?
  • 你还有这个参考。这算作正常参考。
  • 来自 JLS 的报价会给您 +1(如果我能找到,我会稍后添加)。
  • 请做。我没兴趣看。
【解决方案2】:

大部分垃圾收集是透明的。它可以消除手动内存管理的不必要的复杂性。因此,它看起来不会被收集,但实际发生的事情更加微妙。

简单来说,编译器可能会完全忽略对象的构造。 (通过编译器,我的意思是比 javac 更低级别的编译器。字节码将是源的字面音译。)更模糊的是,垃圾收集通常在单独的线程中运行,并且实际上将未访问的对象作为正在运行的方法删除。

如何观察到这一点?终结者中的常见嫌疑人。它可以与对象上运行的方法同时运行。通常,您可以在终结器和普通方法中使用 synchronized 块来解决这个问题,这引入了必要的 happens-before 关系。

【讨论】:

    【解决方案3】:

    m 只是一个存储了引用的变量。这将被程序员用来进一步使用同一个对象在同一个对象上编写逻辑。

    在执行时,程序将被转换为 OP-CODES / INSTRUCTIONS 。 这些指令将引用对象(毕竟它是一个内存位置)。 如果 m 存在,将通过 INDIRECT REFERENCE 访问对象的位置。 如果 m 不存在,则参考为 DIRECT。

    所以在这里,对象被 CPU 寄存器使用,而与引用变量的使用无关。

    在执行流程在 main() 函数的范围内之前,这将是可用的。

    此外,根据 GC 过程,GC 只会从内存中删除对象,一旦 GC 确定该对象将不再被使用。

    每个对象都有机会生存多次(取决于情况和算法)。一旦机会的数量结束,那么只有对象被垃圾收集。

    简单来说,最近使用的对象将有机会留在内存中。 旧对象将从内存中删除。

    所以给出你的代码:

    public class Main {
    
    public void run() {
        // ...
    }
    
    public static void main(String[] args) {
        new Main().run();
    }
    }
    

    对象不会被垃圾回收。

    另外,例如,请尝试查看匿名类示例。或者来自 AWT / SWING 中事件处理的示例。

    在那里,你会发现很多这样的用法。

    【讨论】:

      【解决方案4】:

      接受的答案不正确。对象是否可以被 GC 取决于您的 public void run() {// ...} 方法是否引用了类实例(this)。试试:

      public class FinalizeThis {
      
          private String a = "a";
      
          protected void finalize() {
              System.out.println("finalized!");
          }
      
          void loop() {
              System.out.println("loop() called");
              for (int i = 0; i < 1_000_000_000; i++) {
                  if (i % 1_000_000 == 0)
                      System.gc();
              }
              // System.out.println(a);
              System.out.println("loop() returns");
          }
      
          public static void main(String[] args) {
              new FinalizeThis().loop();
          }
      }
      

      上面的程序总是输出

      loop() called
      finalized!
      loop() returns
      

      在 Java 8 中。但是,如果您取消注释 System.out.println(a),输出将更改为

      loop() called
      a
      loop() returns
      

      这次没有GC,因为调用的方法引用了实例变量(this.a)。

      你可以看看this answer

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-08
        • 2012-03-26
        • 2011-07-11
        • 1970-01-01
        • 2019-09-04
        • 2012-03-28
        • 1970-01-01
        • 2011-07-16
        相关资源
        最近更新 更多