2012-07-19 89 views
6

此问题取自Kathy SierraSCJP 1.6。有多少个对象符合垃圾回收的条件?有资格垃圾回收的对象

根据Kathy Sierra的回答,它是C。这意味着两个对象有资格进行垃圾回收。我已经给出了答案的解释。 但为什么c3不符合garbage collection(GC)的资格?

class CardBoard { 
    Short story = 200; 
    CardBoard go(CardBoard cb) { 
    cb = null; 
    return cb; 
} 

public static void main(String[] args) { 
    CardBoard c1 = new CardBoard(); 
    CardBoard c2 = new CardBoard(); 
    CardBoard c3 = c1.go(c2); 
    c1 = null; 
    // Do stuff 
} } 

当达到// Do stuff,有多少个对象资格GC?

  • A:0
  • B:1
  • C:2
  • d:编译失败
  • E:这是不可能知道
  • F:一个例外是在运行时引发

答:

  • C是正确的。只有一个CardBoard对象(c1)符合条件,但它具有相关联的Short包装器对象,该对象也符合条件。
  • 基于上述内容,A,B,D,E和F不正确。 (目标7.4)
+0

严格来说'c3'不能符合GC的条件,因为*它不是一个对象*。它是一个变量可以指向一个对象。 – 2012-07-19 15:35:48

+0

正确的答案是[所有人](http://stackoverflow.com/a/26645534/2711488)... – Holger 2016-05-02 09:10:49

回答

6

c3指向的任何对象都不存在。构造函数只被调用两次,两个对象,分别由c1c2指向。 c3只是一个引用,它从来没有被分配过任何东西,只是空指针。

当前指向null的参考c3不会超出范围并从堆栈中移除,直到主方法结束处的右大括号超出范围。因为c1基准设置为空,但c2参考尚未改变,因此分配给它的对象仍然是经由c2参考这个范围可达

原来分配给c1对象是不可达。

+0

这是一个有趣的事情,有这么多的教程和练习讨论这样的事情,他们都是错误的。事实是,JVM根本不知道局部变量的范围。过去,这通常意味着有些参考文献虽然超出范围,但并未被清除。今天,您可能会对以下事实感到惊讶:即使在“范围内”仍收集对象,请参阅[“在Java 8中调用强制可达对象的finalize()”](http://stackoverflow.com/q/26642153/2711488 )... – Holger 2016-05-02 09:15:04

+0

1)这个问题是关于java 6的2)不知道'// do stuff'可能包含什么,不可能推测编译器可能会对c2的未来可达性做出哪些优化结论。 – Affe 2016-05-03 16:46:54

+0

Java 6和Java 8之间的规则没有改变。你甚至不能排除在实际的Java 6实现中会发生这样的事情,只是它迄今尚未在SO上进行讨论。如果你认为'//做东西'可能包含与可达性有关的行为,那么正确的答案是这个问题是不完整的。这只是证明更多,这样的问题是多么毫无意义...... – Holger 2016-05-03 17:43:42

4

c3null,所以很明显,没有对象有资格进行垃圾回收。

注意,创建只有两个CardBoard对象,这两个在这些线路上:

CardBoard c1 = new CardBoard(); 
CardBoard c2 = new CardBoard(); 

和参考杂耍后,只有其中之一是没有引用。

-1

如果您发现代码中只创建了两个对象。 c3从不初始化为一个对象,它是一个空引用。因此,只有一个“对象”有资格进行垃圾回收。

7

让我们通过线打破这行:

CardBoard c1 = new CardBoard(); 

我们现在有两个对象,在CardBoardc1点和Shortc1.story。无论是供GC截至CardBoardc1点和CardBoard点在Shortstory变量...

CardBoard c2 = new CardBoard(); 

与上述类似,我们现在有四个对象,其中没有一个是可供GC。

CardBoard c3 = c1.go(c2); 

我们调用的方法去纸板指向由c1,路过的c2的值,它是一个CardBoard对象的引用。我们将参数归为空,但是Java通过值意味着c2变量本身不受影响。然后我们返回空参数。 c3nullc1c2不受影响。我们仍然有4个对象,其中没有一个可以用GC'd。

c1 = null; 

我们为null c1。这c1以前在现在所指向的对象CardBoard没有任何指向它,它可以GC'd。因为CardBoard对象内部story变量在Short指向的唯一的事情,因为这CardBoard对象符合GC的Short也变得适合GC。这给了我们4个物体,其中2个可以GC'd。资格GC的对象是以前由c1c1.story引用的那些。

1

的形式上正确的答案是,我们不知道。我们不知道原因是这一行:

Short story = 200; 

这将编译到后面的字节代码:

CardBoard(); 
Code: 
    0: aload_0 
    1: invokespecial #1     // Method java/lang/Object."<init>":()V 
    4: aload_0 
    5: sipush  200 
    8: invokestatic #2     // Method java/lang/Short.valueOf:(S)Ljava/lang/Short; 
    11: putfield  #3     // Field story:Ljava/lang/Short; 
    14: return 

8号线是这里的关键,Short.valueOf(),它返回的盒装相当于原始的200。让我们来看看Short.valueOf()的Javadoc:在-128到127, 以下的范围

此方法将总是缓存值,并可以缓存在这个范围之外的其他值。

200不在“必须缓存”范围内,因此属于“可能缓存”范围。如果缓存,当其含有CardBoard实例的story值将不符合GC。如果没有缓存,story将无法​​访问,因此GCed。

为了使问题明确(和提议的答案是正确的),代码应修正如下:

Short story = new Short(200); 

更新:1.6 JavadocShort.valueOf()是不是1.8版本我引用了更多神秘,但是也适用相同的逻辑:仅通过查看代码是否将返回新的或缓存的实例Short就无法确定。