从垃圾收集中不直观地驱逐对象

Etk*_*tki 5 java garbage-collection completable-future

我正在调试内存泄漏,并且不得不深入了解 CompletableFuture 的内部结构。有这段代码(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;
Run Code Online (Sandbox Code Playgroud)

代码本身对我来说非常清楚:应用一个返回 CompletionStage ( g) 的函数,创建一个中继,最终将值传输到另一个 CompletableFuture ( d) ,然后返回另一个 future ( d) 。我看到以下参考情况:

  • copy引用dand g(构造函数中没有魔法,只有字段赋值)
  • g参考copy
  • d没有引用任何内容

d返回,因此,事实上,对我来说,gcopy似乎都是内部方法变量,(乍一看)永远不应该离开该方法并最终被GC。幼稚的测试和它是由经过验证的开发人员很久以前编写的事实都表明我错了并且遗漏了一些东西。是什么原因导致这些对象被垃圾收集忽略?

Hol*_*ger 5

在引用的代码中,没有任何东西可以阻止这些 future 的垃圾收集,也没有必要这样做。这段代码适用于第一个CompletableFuturethis实例)已经完成并且CompletableFuture直接计算的 compose 函数返回的结果尚未完成的情况。

\n\n

现在,有两种可能的情况

\n\n
    \n
  1. 正在进行完成尝试。然后,最终完成 future 的代码将保存对它的引用,并且在完成时,它将触发依赖阶段的完成(通过 注册g.push(copy))。在这种情况下,依赖阶段不需要保存对其前提阶段的引用。

    \n\n

    这是一般模式。如果存在链x --will complete-\xe2\x86\x92 y,则不会有 fromy到 的引用x

  2. \n
  3. 没有其他对该CompletableFuture实例的引用g,并且g尚未完成。在这种情况下,它根本不会完成,并且在g内部保留引用不会改变这一点。那样只会浪费资源。

  4. \n
\n\n

下面的示例程序将说明这一点:

\n\n
public static void main(String[] args) throws Throwable {\n    ReferenceQueue<Object> discovered = new ReferenceQueue<>();\n    Set<WeakReference<?>> holder = new HashSet<>();\n\n    CompletableFuture<Object> initial = CompletableFuture.completedFuture("somevalue");\n\n    CompletableFuture<Object> finalStage = initial.thenCompose(value -> {\n        CompletableFuture<Object> lost = new CompletableFuture<>();\n        holder.add(new WeakReference<>(lost, discovered));\n        return lost;\n    });\n    waitFor(finalStage, holder, discovered);\n    finalStage = initial.thenCompose(value -> {\n        CompletableFuture<Object> saved = CompletableFuture.supplyAsync(()-> {\n            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));\n            return "newvalue";\n        });\n        holder.add(new WeakReference<>(saved, discovered));\n        return saved;\n    });\n    waitFor(finalStage, holder, discovered);\n}\nprivate static void waitFor(CompletableFuture<Object> f, Set<WeakReference<?>> holder,\n                    ReferenceQueue<Object> discovered) throws InterruptedException {\n    while(!f.isDone() && !holder.isEmpty()) {\n        System.gc();\n        Reference<?> removed = discovered.remove(100);\n        if(removed != null) {\n            holder.remove(removed);\n            System.out.println("future has been garbage collected");\n        }\n    }\n    if(f.isDone()) {\n        System.out.println("stage completed with "+f.join());\n        holder.clear();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

传递给的第一个函数thenCompose创建并返回一个新的未完成的函数CompletableFuture,而不尝试完成它,不保存或存储对其的任何其他引用。相反,第二个函数创建提供 a 的CompletableFuture过孔,该过孔将在一秒钟后返回一个值。supplyAsyncSupplier

\n\n

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

\n\n
public static void main(String[] args) throws Throwable {\n    ReferenceQueue<Object> discovered = new ReferenceQueue<>();\n    Set<WeakReference<?>> holder = new HashSet<>();\n\n    CompletableFuture<Object> initial = CompletableFuture.completedFuture("somevalue");\n\n    CompletableFuture<Object> finalStage = initial.thenCompose(value -> {\n        CompletableFuture<Object> lost = new CompletableFuture<>();\n        holder.add(new WeakReference<>(lost, discovered));\n        return lost;\n    });\n    waitFor(finalStage, holder, discovered);\n    finalStage = initial.thenCompose(value -> {\n        CompletableFuture<Object> saved = CompletableFuture.supplyAsync(()-> {\n            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));\n            return "newvalue";\n        });\n        holder.add(new WeakReference<>(saved, discovered));\n        return saved;\n    });\n    waitFor(finalStage, holder, discovered);\n}\nprivate static void waitFor(CompletableFuture<Object> f, Set<WeakReference<?>> holder,\n                    ReferenceQueue<Object> discovered) throws InterruptedException {\n    while(!f.isDone() && !holder.isEmpty()) {\n        System.gc();\n        Reference<?> removed = discovered.remove(100);\n        if(removed != null) {\n            holder.remove(removed);\n            System.out.println("future has been garbage collected");\n        }\n    }\n    if(f.isDone()) {\n        System.out.println("stage completed with "+f.join());\n        holder.clear();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

表明被放弃的 future 不会被垃圾收集阻止,而另一个将至少保留到完成为止。

\n