Java / weak references : weak reference.get() 不为 null 而没有更多的强引用持有它

Java / weak references : weak reference.get() is not null while there is no any more strong reference holding it

这是我的非常简单的代码:

class UpdateAlertActivity extends Activity {
    O o ;
    WeakReference<O> _o ;
    ArrayList<O> arr = new ArrayList<>();

    static class O {
        private String l ;

        public O(String l) {
            this.l = l ;
        }
    }

    void test()
    {
        arr.clear();
        Runtime.getRuntime().gc();
        Log.i(LOG_TAG, "breakpoint");
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        o = new O("hello");
        arr.add(o);

        _o = new WeakReference<>(o);


        test(); 
    }
}

当我在 test() 方法中清除数组时,我将 _o.get() 排除在我的断点处 null,但事实并非如此。当我在调试器中看到 o 所在的位置时,它仅在弱引用 _o 中显示我,而我认为弱引用是在没有其他强引用时释放它们的实例。 ..

我在 Whosebug 上看到我错过了调用 GC,但是如您所见,调用它对我的程序没有任何影响。

编辑: 即使是这个超级简单的代码也不能像预期的那样工作,这毫无意义...

O o = new O("lol");

WeakReference<O> _o = new WeakReference<>(o);

Log.i(LOG_TAG, "1:" + _o.get().toString());

o = null ;

Log.i(LOG_TAG, "2:" + _o.get().toString());

System.gc();
Log.i(LOG_TAG, "3:" + _o.get().toString()); // output not null here 

编辑2: 我想使用 GC,因为我在另一个静态 class 中有一个名为 lastUpdates 的数组,其中有时充满了我的服务器通过 TCP 发送的应用程序更新。

我所有的活动都观察这个 lastUpdate 数组(通过实现 Observer/Observable 接口),当它发生变化时,所有活动都会收到通知,并使用新到达的更新列表调用特定函数 onUpdate(ArrayList u)作为参数。此函数仅在 activity 恢复时处理更新(在 activity 睡眠期间更改 UI 会导致应用程序崩溃)。

如果整个应用正在“休眠”(例如用户在设备菜单中),我只希望第一个 activity 醒来能够处理新的更新。

为此,我在我的 activity class 中创建了一个数组 pendingPersistentUpdates 存储 activity 休眠时捕获的所有更新的弱引用。当第一个 activity 醒来时,应用程序休眠期间捕获的更新仍在 lastUpdates 数组中,所以我希望 pendingPersistentUpdates activity prop 中存储的弱引用指向 return实际更新,所以我的 activity 可以在 onResume.

UI 处理它们

我预料到了这种情况:

不过,由于 lastUpdate.clear() 不会触发 GC,B pendingPersistentUpdates 更新仍然存在,并且会被第二次处理(不需要的行为)

不可能强制垃圾收集器运行。 Runtime.getRuntime().gc() 只是 System.gc() 的别名,两者都只是一个提示(阅读文档;他们提到了这一点)。特别是,调用 gc() 通常意味着 collection 将在其他某个线程 中发生 ;所以它更像是收藏家的'start sign'; gc() 不一定会暂停并等待 gc 完成一个完整的循环,它只是告诉 gc 开始一个。同样,不能保证,Thread.sleep(10000L); 使您更有可能看到 GC 调用的效果。

A WeakReference 仅保证此 object 的存在不会妨碍它所引用的 object 的任何垃圾 collection。就这样。当到达指示物的唯一方法是通过 WeakReference objects 时,它不会立即失去它的指示物。这将在稍后发生(事实上,这将在 之前发生 object 被 GC:首先,收集器将清除所有弱引用,稍后将 object占用才是真正的免费使用)。因此,您在调试器中观察到的内容(到达 object 的唯一方法是通过该 WR)并没有本质上被破坏。

另请注意,调试器本身可能是问题所在。仅仅在那里并观察它就会产生影响(所以确保你 运行 它也没有)。

如果您希望 GC 更加努力,您可以分配一些相当大的数组并暂停线程,这会有所帮助,但是您无法保证 GC 运行。如果你想观察,运行 java 和 java -verbose:gc restOfArgsHere,并关注控制台;如果 GC 真的发生了,你会看到它。

不过

您粘贴的代码甚至无法编译,这表明您要么只粘贴了一半,要么对它进行了一些编辑 'for clarity'(通常是个坏主意,最好准确粘贴您正在使用的代码!)。特别是,您写 o = new O("hello");,但 o 未在任何地方定义。 如果它是您的 class 的字段,则意味着根本无法收集所指对象! 因此您可能需要检查一下。使 O o = new O("hello"); 而不是你所拥有的。假设您正确阅读了调试器并且它运行正常,这不是它,但它是可疑的。

仅调用垃圾收集器向系统建议它应该释放内存,它不会明确强制执行。这意味着如果有足够的内存,收集可能不会真正发生。

https://developer.android.com/reference/java/lang/System#gc()

Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.

注意下一句“gc 方法建议Java 虚拟机努力回收未使用的对象”

垃圾收集器在内部使用启发式/阈值来决定何时收集未使用的对象,因此,基本上是在 JVM 需要内存时。