7
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,应该有资格进行垃圾收集。

4

5 回答 5

8

第 6 行的变量 t2 不是对该对象的唯一引用。有一个从 t 到对象 t2 的引用,它是在函数 go 中创建的,而对象 t2 又持有对 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 行:su 设置为引用 s2,这意味着对象 A 获取对 C 的引用注意 C 也有对 B 的引用,因此此时存在从 A 到 B 的链

第 15 行返回对象 B 并返回第 5 行

第 5 行:t2 设置为引用对象 B(B 现在被 t2 直接引用两次,一次是因为 t 引用对象 A,而对象 A 引用 C,而 C 引用 B)

第 6 行:引用 t2 设置为 null,因此 B 丢失了一个引用,但 t 仍然存在,指向 A 指 C 指 B

于 2012-12-25T10:32:03.370 回答
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是实际分配的对象o2o3可以很容易地计算出每个点可以回收多少对象;在这种情况下,在第 6 行o1可以从根访问之后,o3可以从 访问o1和从o2访问o3,因此无法回收任何对象。

作为旁注,我注意到您写了“但根据我的说法,至少有一个对象t2,...”。如果这是您需要解决的问题,我建议您摒弃通过指向对象的变量来命名对象的习惯;而是给每个对象一个虚构的 id,就像我在上面对 , 所做的那样o<n>,并将变量视为指向这些对象的指针而不是它们的名称。这是因为,就像指针和不同的名称一样,一个对象可能有多个或少于一个与之关联的变量,并且关联变量的列表可以随时更改。

于 2012-12-25T11:03:58.350 回答
2

在第 5 行,您正在调用方法Tahiti.go(),因此程序从第 5 行跳转到第 10 行,并在 6 处到达 11。

于 2012-12-25T10:27:53.200 回答
1

第 11 行在第 6 行之前执行

于 2012-12-25T10:27:08.203 回答
0

堆堆栈图是确定哪些对象符合垃圾回收条件的最佳方法:

以下是上述代码的堆栈图:

1000x: TahitiObj: t:2000x

2000x: TahitiObj: 5000x

3000x: TahitiObj: t: 4000x

4000x: TahitiObj: 5000x

5000x: TahitiObj: t: 6000x

6000x: TahitiObj: 3000x

Main_Stack

args = null

t = 1000x

t2 = null

虽然 Go_Stack 会在执行完成后从内存中移除。为了完整起见,我将其保留在这里。

Go_Stack

t = 1000x

t1 = 3000x

t2 = 5000x

通过查看 Main_Stack 和 HEAP 可以观察到,我们可以直接或间接地访问所有对象。

因此,当代码执行到 side main 方法中的第 7 行时,没有对象有资格进行垃圾回收。

于 2016-08-06T08:43:53.290 回答