1. public class Tahiti {
2. Tahiti t;
3. public static void main(String[] args) {
4. Tahiti t = new Tahiti();
5. Tahiti t2 = t.go(t);
6. t2 = null;
7. // more code here
8. }
9. Tahiti go(Tahiti t) {
10. Tahiti t1 = new Tahiti(); Tahiti t2 = new Tahiti();
11. t1.t = t2; t2.t = t1; t.t = t2;
12. return t1;
13. }
14. }
当到达第7行时,有多少对象符合垃圾回收的条件?
根据此问题的答案,第11行没有符合GC要求的对象; 但据我说,至少有一个对象,t2,在第6行设置为null,应该有资格进行垃圾收集。
答案 0 :(得分:7)
第6行的变量t2不是对象的唯一引用。从t到对象t2的引用是在函数go中创建的,而对象t2又保持对t1的引用,t1是函数go返回的相同对象。因此第6行只是减少了引用的数量,但仍然存在对象的实时引用。
编辑: 让我们尝试更详细的解释,首先我重新编写代码以使解释更容易。每行一个语句和较少混淆的变量名称+我用字母A,B和C标识了三个相关对象。
1. public class Tahiti {
2. Tahiti u;
3. public static void main(String[] args) {
4. Tahiti t = new Tahiti(); // object A
5. Tahiti t2 = t.go(t);
6. t2 = null;
7. // more code here
8. }
9. Tahiti go(Tahiti s) {
10. Tahiti s1 = new Tahiti(); // object B
11. Tahiti s2 = new Tahiti(); // object C
12. s1.u = s2;
13. s2.u = s1;
14. s.u = s2;
15. return s1;
16. }
17. }
在第4行:变量t被初始化以引用新对象。让我们将该对象称为A(它通常在java中没有名称,但为了这个解释,它会更容易)。
在第5行:t传递给函数go,所以我们转到第9行
在第9行:参数s引用在第4行创建的对象A
第10行:将变量s1初始化为指向对象B
第11行:初始化变量s2以引用对象C
第12行:s1.u设置为引用s2,这意味着对象B获得对C
的引用第13行:s2.u设置为引用s1,因此对象C得到对B的引用
第14行:s.u设置为引用s2,这意味着对象A得到对C的引用,注意C也引用了B,所以此时有一个从A到B的链
第15行返回对象B并返回第5行
第5行:t2设置为参考对象B(B现在直接被t2引用两次,一次因为t指的是引用C的引用B的对象A)
第6行:参考t2设置为空,因此B丢失一个参考但是t仍然是活的,指向A指的是C指的是B
答案 1 :(得分:3)
您可以绘制一个表格,该表格在行之间进行映射,并且在行之后立即指向每个对象的访问路径列表,如下所示:
╔════════════╦═══════════════════════╦════════════════╦═══════════════════╗
║ After Line ║ Pointers to o1 ║ Pointers to o2 ║ Pointers to o3 ║
╠════════════╬═══════════════════════╬════════════════╬═══════════════════╣
║ 3 ║ not allocated ║ not allocated ║ not allocated ║
║ 4 ║ main:t ║ not allocated ║ not allocated ║
║ 9 ║ main:t, go:this, go:t ║ not allocated ║ not allocated ║
║ 10 ║ main:t, go:this, go:t ║ go:t1 ║ go:t2 ║
║ 11 ║ main:t, go:this, go:t ║ go:t1, o3.t ║ go:t2, o2.t, o1.t ║
║ 5 ║ main:t ║ main:t2, o3.t ║ o2.t, o1.t ║
║ 6 ║ main:t ║ o3.t ║ o2.t, o1.t ║
╚════════════╩═══════════════════════╩════════════════╩═══════════════════╝
o1
,o2
和o3
是分配的实际对象。可以在每个点轻松计算可以回收多少个物体;在这种情况下,在第6行o1
可以从根访问后,o3
可以从o1
访问,o2
可以从o3
访问,因此没有任何对象可以访问回收
作为旁注,我注意到你写了“但根据我至少有一个对象, t2 ,......”。如果这是你需要解决的问题,我建议放弃用指向它们的变量来命名对象的习惯;而是为每个对象提供一个虚构的id,就像我上面用o<n>
所做的那样,并将变量视为指向这些对象的指针而不是它们的名称。这是因为,与指针和名称不同,对象可能包含多于或少于一个与之关联的变量,并且关联变量列表可以随时更改。
答案 2 :(得分:2)
在第5行,您调用方法Tahiti.go()
,因此程序从第5行跳到10,到6时达到11。
答案 3 :(得分:1)
第11行在第6行之前执行
答案 4 :(得分:0)
堆栈图是确定哪些对象符合垃圾收集条件的最佳方法:
以下是上面代码的堆栈图:
1000x: TahitiObj: t:2000x
2000x: TahitiObj: 5000x
3000x: TahitiObj: t: 4000x
4000x: TahitiObj: 5000x
5000x: TahitiObj: t: 6000x
6000x: TahitiObj: 3000x
args = null
t = 1000x
t2 = null
虽然Go_Stack在执行完成后将从内存中删除。为了完整,我把它保存在这里。
t = 1000x
t1 = 3000x
t2 = 5000x
通过查看Main_Stack和HEAP,可以观察到我们可以直接或间接地访问所有对象。
因此,当代码执行到副主方法中的第7行时,没有对象符合垃圾收集条件。