变量在catch块后立即收集垃圾

Jai*_*zel 7 java garbage-collection

我可以从垃圾收集器中看到这种奇怪的行为

public class A {
    public static void main(String[] args) {

        String foo;
        try {
            foo = "bar";

            int yoo = 5; //1
        } catch (Exception e) {
        }

        int foobar = 3;//2 
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我去调试并在// 1上放置一个断点,则foo不为null且其值为"bar"但在断点中// 2 foo为null,这在调试时很难理解.我的问题是,是否有任何规范说这是垃圾收集器的合法行为

由于这个小变化,它不会收集垃圾:

public class A {
    public static void main(String[] args) {

        String foo;
        try {
            foo = "bar";
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        int foobar = 3;
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么?

ass*_*ias 9

在这种情况下,您在设置后不使用foo变量,因此JVM完全忽略变量甚至是合法的,因为它从未使用过,并且不会改变程序的结果.

但是,这在调试模式下不太可能发生.

在你的情况下,只要foo在范围内或你持有对它的引用(包括try/catch块之后的部分),foo就不应该得到GC.

编辑

实际上我得到的行为与你在Netbeans 7.1.1中用Java 7.0_03描述的行为相同......

一个问题可能是因为您没有设置默认值foo,您不能在try/catch块之后使用它(它不会编译).

Bytcode

  • 使用您使用的代码
public static void main(java.lang.String[]);
Code:
   0: ldc           #2                  // String bar
   2: astore_1      
   3: iconst_5      
   4: istore_2      
   5: goto          9
   8: astore_2      
   9: iconst_3      
  10: istore_2      
  11: return        
Run Code Online (Sandbox Code Playgroud)
  • 使用String foo = null;作为第一个语句,在这种情况下,调试器会在try/catch块之后看到值:
public static void main(java.lang.String[]);
Code:
   0: aconst_null   
   1: astore_1      
   2: ldc           #2                  // String bar
   4: astore_1      
   5: iconst_5      
   6: istore_2      
   7: goto          11
  10: astore_2      
  11: iconst_3      
  12: istore_2      
  13: return        
Run Code Online (Sandbox Code Playgroud)

我不是代码专家,但他们看起来和我很相似......

结论

我个人的结论是,为了让调试器显示其值foo,它必须运行foo.toString()某种类型,这foo可能是catch块之后的有效语句,因为它可能尚未初始化.添加System.out.println(foo)该部分是不合法的(不编译).调试器有点丢失了值和显示的内容null.

为了说服自己这与GC无关,您可以尝试以下示例:

public static void main(String[] args){
    String foo;
    char[] c = null;
    try {
        foo = "bar";
        c = foo.toCharArray();

        int yoo = 5; //1
    } catch (Exception e) {
    }

    int foobar = 3;//2 
}
Run Code Online (Sandbox Code Playgroud)

foobar行,你可以看到,c持有bar,但FOO显示为null.所以String仍然存在,但调试器无法显示它.

更有趣的例子:

public static void main(String[] args){
    String foo;
    List<String> list = new ArrayList<String>();

    try {
        foo = "bar";
        list.add(foo);
        int yoo = 5; //1
    } catch (Exception e) {
    }

    int foobar = 3;//2 

}
Run Code Online (Sandbox Code Playgroud)

foobar行,foo显示为null,但list包含了"bar"...尼斯.