CompletableFuture 链未完成 -> 垃圾收集器?

CompletableFuture Chain uncompleted -> Garbage Collector?

如果我有一个(或多个)CompletableFuture 尚未开始,并且在该方法上有一些 thenApplyAsync()anyOf() 方法。

垃圾收集器会清除所有这些吗?

如果链的末尾有 join()/get() -> 同样的问题:垃圾收集器会删除所有这些吗?

也许我们需要有关 join() 上下文的更多信息。

join 是线程中的最后一个命令,没有副作用。 那么在那种情况下线程是否仍然处于活动状态? - Java Thread Garbage collected or not

无论如何,如果我确定(也许在 try-catch-finally 中),我不会启动那个 Completable-chain,那么向链下推一个毒药丸是个好主意,或者不是有必要吗?

问题是因为这样的事情? (https://bugs.openjdk.java.net/browse/JDK-8160402)

一些与之相关的问题:Thread-Executor 什么时候发出信号来安排新任务?我认为,当 CompletableFuture 进入下一个链接 CompletableFuture 时?所以我必须只进行内存泄漏而不是线程泄漏?

编辑:未启动的 CompletableFuture 是什么意思?

我的意思是 var notStartedCompletableFuture = new CompletableFuture<Object>(); 而不是 CompletableFuture.supplyAsync(....);

我可以这样启动 notStartedCompletableFuture: notStartedCompletableFuture.complete(new Object); 稍后在程序流中或来自另一个线程。

编辑 2:更详细的示例:

AtomicReference<CompletableFuture<Object>> outsideReference=new AtomicReference<>();

final var myOuterThread = new Thread(() ->
{
    final var A = new CompletableFuture<Object>();
    final var B = new CompletableFuture<Object>();

    final var C = A.thenApplyAsync((element) -> new Object());
    final var D = CompletableFuture.anyOf(A, C);

    A.complete(new Object());

    // throw new RuntimeException();

    //outsideReference.set(B);

    ----->B.complete(new Object());<------ Edit: this shouldn't be here, i remove it in my next iteration

    D.join();

});

myOuterThread.start();

//myOutherThread variable is nowhere else referenced, it's sayed so a local variable, to point on my text on it^^

  1. 所以在正常情况下,在我的示例中,我没有外部
    参考。线程中的 CompletableFutures 从来没有机会 完成。通常 GC 可以安全地擦除线程 以及其中的内容 CompetableFutures。但我不 这么认为,这会发生吗?
  2. 如果我通过抛出异常来放弃它 -> join() 永远不会 达到,那么我想所有的东西都会被 GC 删除?
  3. 如果我通过 AtomicReferenceCompletableFutures 中的一个交给外部,那么就有机会解锁 join(),这里应该没有 GC,直到解锁发生了。但! join() 上的等待 myOuterThreadjoin() 之后不必再去那里了。因此,在 completes B 之外的某个人之前,它可能是一种优化删除该线程的方法。但我认为这也不会发生?!

这里还有一个问题,如果线程因等待 join() 而阻塞或返回到线程池,我如何证明该行为?线程也“阻塞”?

如果线程在永远不会完成的 CompletableFuture 上调用 join()get(),它将永远保持阻塞状态(除非它被中断),持有对那个未来。

如果那个未来是后代未来链(+任务和执行者)的根,它也会保留对那些的引用,这些引用也将保留在内存中(以及所有传递引用的对象)。

当通过 then*() 方法创建时,future 通常不会持有对其“parent(s)”的引用,因此如果存在,它们通常应该被垃圾回收没有其他参考资料——但要注意那些,例如调用线程中的局部变量,对 allOf() 之后的 lambda 中使用的 List<CompletableFuture<?>> 的引用等

您似乎正在努力解决 CompletableFuture 可能泄漏的不同方式,具体取决于您创建它的方式。但 创建 的方式、地点、时间或原因并不重要。唯一重要的是它是否仍然可以到达

Will the Garbage Collector remove all of that?

我们希望在两个地方引用 CompletableFuture:

  • Runnable(或其他)中完成未来。
  • 在任何其他会(在某个时候)尝试从未来获取最终值的代码中。

如果您有调用 thenApplyAsync()anyOf(),则引用 Runnable 位于该调用的参数中。如果调用 仍然可以发生 ,那么对 Runnable 的引用必须仍然可以访问。

在你的例子中:

var notStartedCompletableFuture = new CompletableFuture<Object>();

如果一些仍在执行的代码仍然可以访问变量 notStartedCompletableFuture,那么 CompletableFuture 是可以访问的并且不会被垃圾收集。

另一方面,如果 notStartedCompletableFuture 不再可访问,并且如果未来不再可以通过其他路径访问,那么它将根本无法访问...并且将是垃圾收集的候选人。


If there is a join() / get() at the end of that chain -> same question: Will the Garbage Collector remove all of that?

这没什么区别。这一切都基于可达性。 (唯一的问题是,当前处于活动状态的线程1 始终是可达的,无论对其 Thread 对象的任何其他引用如何。这同样适用于其 Runnable,以及其他可从 Runnable.)

访问的对象

但值得注意的是,如果您在永不终止/完成的线程/future 上调用 join() get(),您将阻塞当前线程,可能永远阻塞。 和线程泄漏一样糟糕。

1 - 线程从启动到终止都是“活动的”。


When is the Thread-Executor signaled to schedule a new task?

这取决于你所说的“日程”是什么意思。如果你的意思是什么时候提交任务,那么答案就是调用 submit 的时候。如果你的意思是,它实际上是什么时候 运行 ...它进入队列,当它到达队列头部并且工作线程可以自由执行它时它 运行s 。

thenApplyAsync()all_of() 的情况下,任务在相应的方法调用发生时提交(即发生 submit(...) 调用)。因此,例如,如果根据先前调用的结果调用 thenApplyAsync,则该调用必须首先 return。

这都是 Java 表达式求值的基本属性的结果...应用于您用来构建阶段链的表达式。


一般来说,您不需要try / finallytry with resources 来清除潜在的内存泄漏。

您需要做的就是确保您不会在变量、数据结构等中保留对各种未来、阶段等的引用,这些引用将在您的计算生命周期之后保持可访问/可达。如果你这样做...... 那些引用 可能是泄漏的来源。

线程泄漏不应该是您关心的问题。如果您的代码没有显式创建线程,它们将由执行程序服务/池管理。

此答案 解决了您“编辑 2”中的 3 个后续问题。

  1. So in the normal case here in my example i don't have a outside reference.

我假设您指的是带有注释掉语句的版本。

The CompletableFutures in the thread have never a chance getting completed.

不正确。首先,A到这里就完成了:

A.complete(new Object());
    

下一个B在这里完成:

B.complete(new Object());

然后你打电话给D.join()。由于 D 是一个 anyOf 阶段,因此在 AC 中的任何一个完成时完成。 A 已经完成,因此 D.join() 可能不需要等待 C 完成。但由于 C 异步应用函数,它也可以立即完成。

Normally the GC can safely erase both the thread and and the content in there, the CompletableFutures. But I don't think so, that this would happen?

D.join()returns时,线程终止。到那时,它的本地局部变量(ABCD)将无法访问。


  1. If I abort this by throwing an exception -> the join() is never reached, then i think all would be erased by the GC?

A 像以前一样完成,但 BCD 没有。

但是,异常会终止线程,因此局部变量 ABCD 将变得无法访问。


  1. If I give one of the CompletableFutures to the outside by that AtomicReference, there then could be an chance to unblock the join().

三分:

  • AtomicReference 已分配 B,因此 D 上的 join() 不受影响。

  • 正如我们在上面看到的,outsideReference.value() 上的假设 join() 对于变量 A、[=14 是否发生并不重要=]、CD。无论线程以何种方式终止,这些变量都变得不可访问。

  • 但是,您现在已将对 CompletableFuture 对象之一的引用分配给一个与线程具有不同生命周期的变量。 可能 意味着 CompletableFuture 对象在线程终止后仍可访问。