我可以从垃圾收集器中看到这种奇怪的行为
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
}
}
如果我去调试并在//上设置断点,则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;
}
}
为什么?
答案 0 :(得分:9)
在这种情况下,您在设置之后不使用foo变量,因此JVM完全忽略变量甚至是合法的,因为它从未使用过,并且不会改变程序的结果。 / p>
然而,在调试模式下不太可能发生这种情况。
在你的情况下,只要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
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
我不是代码专家,但他们看起来和我很相似......
结论
我个人的结论是,为了让调试器显示foo
的值,它必须运行某种foo.toString()
,这不是catch块之后的有效语句{{1可能尚未初始化。在该部分中添加foo
是不合法的(不编译)。调试器有点丢失,显示值是什么,并显示System.out.println(foo)
。
为了说服自己这与GC无关,您可以尝试以下示例:
null
在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
}
行,您可以看到foobar
持有c
但foo显示为bar
。所以String仍然存在,但是调试器无法显示它。
更有趣的例子:
null
在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
}
行,foobar
显示为foo
,但null
包含list
...很好。
答案 1 :(得分:2)
foo
从不具有默认值,当您转到第2行时,您将超出设置范围。
答案 2 :(得分:2)
生成的字节代码如下:
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
Exception table:
from to target type
0 5 8 Class java/lang/Exception
我的第一个猜测是,我们为foo和foobar重用了局部变量位置,在这种情况下,调试时不再显示该值。但是可以看出本地1没有被覆盖(yoo和foobar虽然共享相同的空间)。
由于这种情况没有发生,我们可以非常肯定JIT在这里没有做任何事情,这是非常奇怪的行为。
答案 3 :(得分:-2)
这不是合法行为,因为主要方法的上下文仍然存在。除非你在try块中声明了String foo,否则理想情况下不应该获取GCed(在这种情况下,它将无法在2处访问)。
我尝试了这段代码,它对我来说很好。