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

huangapple go评论66阅读模式
英文:

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中,尽管我认为弱引用是为了在没有其他强引用时释放它们的实例...

我在StackOverflow上看到我忘记调用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()); // 此处输出不为null

编辑2:
我之所以想使用GC,是因为在另一个静态类中,有一个名为lastUpdates的数组,该数组通过TCP由我的服务器发送的应用程序更新进行填充,有时会填充。

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

如果整个应用程序处于“休眠”状态(例如,用户在设备菜单中),我希望只有第一个唤醒的活动能够处理新的更新。

为此,我在我的活动类中创建了一个数组pendingPersistentUpdates,其中存储了活动休眠期间捕获的所有更新的弱引用。当第一个活动唤醒时,应用程序休眠时捕获的更新仍然在lastUpdates数组中,因此我期望存储在pendingPersistentUpdates活动属性中的弱引用会返回实际的更新,以便我的活动可以在onResume时对其进行UI处理。

我期望的情况是:

  • 活动A运行,活动B暂停(例如在活动A后面)
  • 应用程序接收到更新U。通知所有活动(A和B)。A处理更新,因为它正在运行,如预期所示。B将此更新存储在其pendingPersistentUpdates中,因为它暂停了。
  • A暂停,用户返回到B。在A暂停时,lastUpdates数组被清除,因此B pendingPersistentUpdates中的更新的弱引用将返回null。
  • B恢复,但pendingPersistentUpdates中的弱引用为null(lastUpdates已被清除),因此不会处理U。

然而,由于lastUpdate.clear()不会触发GC,B pendingPersistentUpdates中的更新仍然存在,并且会被处理两次(不希望的行为)。

英文:

Here is my very simple code :

class UpdateAlertActivity extends Activity {
    O o ;
    WeakReference&lt;O&gt; _o ;
    ArrayList&lt;O&gt; arr = new ArrayList&lt;&gt;();

    static class O {
        private String l ;

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

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

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

        o = new O(&quot;hello&quot;);
        arr.add(o);

        _o = new WeakReference&lt;&gt;(o);


        test(); 
    }
}

As I clear array in test() method, I was excepting _o.get() to be null at my breakpoint, but it's not the case. When I see in the debbuger where o is held, it shows me only in the weak reference _o, while I thought weak reference are made to free their instance when there is no other strong reference on them...

I saw on StackOverflow that I missed calling GC, but calling it had no effect in my program as you can see.

EDIT :
Even this super simple code does not work as excpected, it makes no sense...

O o = new O(&quot;lol&quot;);

WeakReference&lt;O&gt; _o = new WeakReference&lt;&gt;(o);

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

o = null ;

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

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

EDIT2 :
I wanted the use of GC because I have, in another static class, an array called lastUpdates, which is filled with app updates sent by my server wia TCP, sometimes.

All my activities observes this lastUpdate array (by implementing Observer/Observable interface), and when it is changed, all are notified, and call a particular function onUpdate(ArrayList<Update> u) with the list of new arrived updates as a parameter. This function process the updates only if the activity is resumed (changing UI during activity sleep causes app to crash).

If the entiere app is "sleeping" (user is in device menu for example), I want only the first activity waking up to be able to process new updates.

For that, I created in my activity class a array pendingPersistentUpdates storing weak references of all updates that has been catch when activity was sleeping. When the first activity wakes up, the updates catch during app sleep time are still in lastUpdates array, so I expect the weak references stored in pendingPersistentUpdates activity prop to return the actual updates, so my activity can UI-process them, at onResume.

I was expecting this situation :

  • An activity A run, an activity B is paused (behind activity A for example)
  • App receives an update U. All activities (A and B) are notified. A process the update because it's running, as expected. B store this update in its pendingPersistentUpdates, because it's paused.
  • A pause, user returns to B. At A pause, lastUpdates array is cleared, so the weak references of the updates in B pendingPersistentUpdates would return null
  • B resume, but pendingPersistentUpdates weak references are null (lastUpdates has been cleared), so U is not processed.

Though, as lastUpdate.clear() does not fire GC, B pendingPersistentUpdates updates still exist, and are processed a second time (behavior not desired)

答案1

得分: 1

不可能强制运行垃圾回收器。Runtime.getRuntime().gc() 只是 System.gc() 的别名,它们都只是一个提示(阅读文档;文档中提到了这一点)。特别地,调用 gc() 通常意味着垃圾收集将在某个其他线程中发生;因此它更像是收集器的“启动信号”;gc() 不一定会暂停并等待垃圾回收完成一个完整的循环,它只是告诉垃圾收集器启动一个循环。再次强调,不能保证,Thread.sleep(10000L); 使得你更有可能看到垃圾回收调用的效果。

WeakReference 仅仅保证了该对象的存在不会妨碍对所引用对象的任何垃圾回收。仅此而已。当唯一获取引用对象的方式是通过 WeakReference 对象时,它不会立即失去引用对象。这将在稍后发生(实际上,这将在对象被垃圾回收之前发生:首先,收集器将清除所有的弱引用,然后稍后将释放对象占用的内存以供其他用途)。因此,你在调试器中观察到的情况(唯一获取对象的方式是通过 WR)并没有本质上的问题。

还要注意,调试器本身可能是问题所在。仅仅存在并观察它可能会产生影响(因此确保你也在没有调试器的情况下运行它)。

如果你希希望垃圾收集器更加努力,你可以分配一些相当大的数组并暂停线程,这可能有所帮助,但是没有什么可以保证会运行垃圾回收。如果你想要观察,可以使用 java -verbose:gc restOfArgsHere 运行 Java,然后关注控制台;如果实际发生了垃圾回收,你会看到的。

然而,

你粘贴的代码甚至不能编译,这表明你要么只粘贴了一半,要么对其进行了一些“为了清楚起见”的编辑(通常不是一个好主意,最好粘贴你正在使用的代码!)。特别地,你写了 o = new O("hello");,但是 o 并没有在任何地方定义。如果它是你的类的字段,这将意味着引用对象根本无法被回收!因此,你可能需要检查一下。将其改为 O o = new O("hello");,而不是你的原文中的内容。假设你正确地阅读了调试器并且它正常工作,这可能不是问题所在,但是确实有些可疑。

英文:

It is not possible to force the garbage collector to run. Runtime.getRuntime().gc() is just an alias for System.gc(), and both are just a hint (read the docs; they mention this). In particular, calling gc() usually means the collection will occur in some other thread; so it's more the 'start sign' for the collector; gc() doesn't necessarily pause and wait around for the gc to finish a full cycle, it merely tells the gc to start one. Whilst, again, no guarantee, Thread.sleep(10000L); makes it more likely you'll see the effects of the GC call.

A WeakReference merely guarantees that the existence of this object won't impede on any garbage collection of the object it refers to. That's all. It doesn't instantaneously lose its referent when the only way to get to the referent is via WeakReference objects. That'll happen later (in fact, that will happen before the object is GCed: First the collector will wipe out all weakrefs, and sometime later will the memory that the object occupied be truly free for use). So, what you're observing in your debugger (that the only way to get to the object is via that WR) isn't inherently broken.

Note also that the debugger itself can be the issue. Merely being there and observing it can have an effect (so make sure you run it without as well).

If you want the GC to try harder, you can allocate some fairly large arrays and pause the thread, that can help, but there is nothing you can do to guarantee a GC run. If you want to observe, run java with java -verbose:gc restOfArgsHere, and keep an eye on the console; if a GC actually occurs, you'll see it.

However.

Your code as pasted doesn't even compile which suggests you either pasted only half of it, or you edited it some 'for clarity' (often a bad idea, best to paste precisely what you're working with!). In particular, you write o = new O(&quot;hello&quot;);, but o isn't defined anywhere. If it is a field of your class, it would imply the referent cannot be collected at all! So you might want to check on that. Make that O o = new O(&quot;hello&quot;); instead of what you have. Assuming you read your debugger correctly and it's functioning properly, this isn't it, but it is suspicious.

答案2

得分: 1

调用垃圾回收器仅建议系统应该释放内存,而不会强制显式地执行。这意味着如果有足够的内存,实际上可能不会进行垃圾回收。

> 调用gc方法建议Java虚拟机努力回收未使用的对象,以便使它们当前占用的内存可用于快速重用。当从方法调用中返回控制权时,Java虚拟机已经尽最大努力从所有丢弃的对象中回收了空间。

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

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

英文:

Calling the garbage collector only suggests to the system that it should free up memory, it does not force it explicitly. This means that the collection may not actually happen if there is enough memory.

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.

Notice the next sentence "the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects"

Internally the garbage collector uses heuristics / thresholds to decide when to collect unused objects, so, basically when the JVM needs memory.

huangapple
  • 本文由 发表于 2020年10月15日 19:57:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/64371088.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定