垃圾回收中非直观的对象逐出 [英] Non-intuitive object eviction from garbage collection

查看:98
本文介绍了垃圾回收中非直观的对象逐出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在调试内存泄漏,因此不得不深入研究CompletableFuture内部。有这段代码(CompletableFuture.uniComposeStage):

I'm debugging a memory leak and had to dive into CompletableFuture internals. There is this piece of code (CompletableFuture.uniComposeStage):

CompletableFuture<V> g = f.apply(t).toCompletableFuture();
...
CompletableFuture<V> d = new CompletableFuture<V>();
UniRelay<V> copy = new UniRelay<V>(d, g);
g.push(copy);
copy.tryFire(SYNC);
return d;

代码本身对我很清楚:应用返回CompletionStage( g ),创建一个中继,该中继最终会将值转移到另一个CompletableFuture( d ),然后返回另一个期货( d )。我看到以下参考情况:

The code itself is quite clear to me: apply a function that returns CompletionStage (g), create a relay that eventually will transfer value to another CompletableFuture (d), then return this another future (d). I see following reference situation:


  • 副本都引用了 d g (构造函数中没有魔术,只有字段分配)

  • g 引用副本

  • d 没有引用

  • copy references both d and g (and there is no magic in constructor, only field assignments)
  • g references copy
  • d references nothing

仅返回 d ,因此,实际上两者对我来说, g copy 似乎是内部方法变量,(乍一看)绝不应该离开方法而应该最终gc'd。幼稚的测试以及它由久经考验的开发人员在很久以前编写的事实都表明我错了,缺少了一些东西。使那些对象从垃圾回收中被忽略的原因是什么?

Only d is returned, so, in fact, both g and copy seem as internal method variables to me, that (on first sight) should never leave the method and be eventually gc'd. Both naive testing and the fact that it was written long ago by proven developers suggests me that i'm wrong and missing something. What is the reason that make those objects being omitted from garbage collection?

推荐答案

在引用的代码中,没有什么阻止垃圾回收的这些期货,没有必要。该代码适用于以下情况:第一个 CompletableFuture this 实例)已经完成并且<$直接评估的compose函数返回的c $ c> CompletableFuture 尚未完成。

In the cited code, there is nothing preventing garbage collection of these futures and there is no need to. This code in question applies to the scenario that the first CompletableFuture (the this instance) has been completed and the CompletableFuture returned by the directly evaluated compose function has not completed yet.

现在,有两种可能的情况

Now, there are two possible scenarios


  1. 正在尝试完成。然后,最终将完成的代码将对其进行引用,并在完成时触发依赖阶段的完成(通过 g.push(copy))。在这种情况下,不需要从属阶段对其先决条件阶段进行引用。

  1. There is an ongoing completion attempt. Then, the code which will eventually complete the future will hold a reference to it and when completing, it will trigger the completion of the dependent stages (registered via g.push(copy)). In this scenario, there is no need for the dependent stage to hold a reference to its prerequisite stage.

这是一种通用模式。如果有一个链 x-将完成-→y ,则不会从 y x

This is a general pattern. If there is a chain x --will complete-→ y, there will be no reference from y to x.

没有其他引用 CompletableFuture 实例 g g 尚未完成。在这种情况下,它根本不会完成,并且内部引用 g 不会改变它。只会浪费资源。

There is no other reference to that CompletableFuture instance g and g has not been completed yet. In this case, it will never be completed at all and holding a reference to g internally wouldn’t change that. That would only waste resources.

以下示例程序将对此进行说明:

The following example program will illustrate this:

public static void main(String[] args) throws Throwable {
    ReferenceQueue<Object> discovered = new ReferenceQueue<>();
    Set<WeakReference<?>> holder = new HashSet<>();

    CompletableFuture<Object> initial = CompletableFuture.completedFuture("somevalue");

    CompletableFuture<Object> finalStage = initial.thenCompose(value -> {
        CompletableFuture<Object> lost = new CompletableFuture<>();
        holder.add(new WeakReference<>(lost, discovered));
        return lost;
    });
    waitFor(finalStage, holder, discovered);
    finalStage = initial.thenCompose(value -> {
        CompletableFuture<Object> saved = CompletableFuture.supplyAsync(()-> {
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
            return "newvalue";
        });
        holder.add(new WeakReference<>(saved, discovered));
        return saved;
    });
    waitFor(finalStage, holder, discovered);
}
private static void waitFor(CompletableFuture<Object> f, Set<WeakReference<?>> holder,
                    ReferenceQueue<Object> discovered) throws InterruptedException {
    while(!f.isDone() && !holder.isEmpty()) {
        System.gc();
        Reference<?> removed = discovered.remove(100);
        if(removed != null) {
            holder.remove(removed);
            System.out.println("future has been garbage collected");
        }
    }
    if(f.isDone()) {
        System.out.println("stage completed with "+f.join());
        holder.clear();
    }
}

第一个传递给的函数thenCompose 会创建并返回一个新的未完成的 CompletableFuture ,而不会尝试完成它,不保存也不存储对其的任何其他引用。相比之下,第二个函数通过 supplyAsync 创建 Supplier CompletableFuture $ c>会在一秒钟后返回值。

The first function passed to thenCompose creates and returns a new uncompleted CompletableFuture without any attempt to complete it, not holding nor storing any other reference to it. In contrast, the second function creates the CompletableFuture via supplyAsync providing a Supplier which will return a value after a second.

在我的系统上,它始终打印

On my system, it consistently printed

future has been garbage collected
stage completed with newvalue


完成

表明不会阻止废弃的将来进行垃圾回收,而另一个将至少保留到完成。

showing that the abandoned future will not be prevented from garbage collection while the other will be held at least until completion.

这篇关于垃圾回收中非直观的对象逐出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆