gpt4 book ai didi

java - 堆栈上无法访问的对象不能被垃圾回收

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:14:52 25 4
gpt4 key购买 nike

出乎我的意料,下面的程序

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.List;

public class StackTest {
public static void main(String[] args) {
Object object1 = new Object();
Object object2 = new Object();
List<Object> objects = Arrays.asList(object1, object2);

WeakReference<Object> ref1 = new WeakReference<>(object1);
WeakReference<Object> ref2 = new WeakReference<>(object2);

for (Object o : objects) {
System.out.println(o);
}
objects = null;

object1 = null;
object2 = null;

System.gc();
System.gc();
System.gc();

System.out.println("ref1: " + ref1.get());
System.out.println("ref2: " + ref2.get());
}
}

仍然打印出来

ref1: java.lang.Object@15db9742
ref2: java.lang.Object@6d06d69c

意思是 object1object2 不是 GC-ed。

但是,当从程序中删除 for 循环时,这些对象可以被 GC 处理并打印程序。

ref1: null
ref2: null

for 循环移动到一个单独的方法具有相同的效果:对象在程序结束时被 GC-ed。

我怀疑正在发生的事情是 for 循环将这些对象存储在堆栈中,并且之后不会删除它们。由于该对象仍存在于堆栈中,因此无法对其进行 GC。

看字节码(我确实不太擅长)似乎支持这个假设:

53: invokeinterface #6,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
58: astore 6
60: aload 6
62: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
67: ifeq 90
70: aload 6
72: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
77: astore 7
79: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
82: aload 7
84: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
87: goto 60

我看到了 astore 命令,但我无法在字节码中找到再次从堆栈中删除这些命令的位置。

但是,我的理论有两个问题:

  • 根据我对该字节码的理解,我预计 object1 已从堆栈中移除(被 object2 覆盖),并且只有最后一个访问的对象循环 (object2) 不会被 GC-ed。
  • for 循环更改为

    for (Object o : objects) {
    System.out.println(o);
    o = null;
    }

    不会改变程序的输出。我原以为这会清除对堆栈中对象的引用。

问题:有人对为什么 for 循环确保这些对象不能被 GC 有可靠的理论吗?我的理论中存在一些漏洞。

上下文:我们在基于 Netbeans 方法检测内存泄漏的单元测试中遇到了这个问题 NBTestCase#assertGC .当对象仍在堆或堆栈上被引用时,此 assertGC 方法将失败。

在我们的测试中,我们有这样的代码

@Test
public void test(){
List<DisposableFoo> foos = ...;

doStuffWithFoo(foos);

List<WeakReference<DisposableFoo>> refs = ...;

for(DisposableFoo foo : foos){
disposeFoo(foo);
}

foos = null;
assertGC(refs);
}

一直失败,直到我们删除了 for 循环。

我们已经有了解决方法(将 for 循环移至单独的方法),但我想了解为什么我们的原始代码不起作用。

最佳答案

问题是您在堆栈上仍然有一个列表迭代器,并且该列表迭代器具有对原始列表的引用。这使列表保持 Activity 状态,就像您从未设置过 objects 一样。为空。

迭代器必须保留对原始集合的引用,以便它可以请求下一个 项等。对于常规Iterator<E>它可以可能将其内部引用设置为null一次hasNext()已返回 false,但对于列表迭代器,情况并非如此,因为您可以在列表迭代器内双向移动。

关于java - 堆栈上无法访问的对象不能被垃圾回收,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45476773/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com