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^^
- 所以在正常情况下,在我的示例中,我没有外部
参考。线程中的 CompletableFutures
从来没有机会
完成。通常 GC 可以安全地擦除线程
以及其中的内容 CompetableFutures
。但我不
这么认为,这会发生吗?
- 如果我通过抛出异常来放弃它 ->
join()
永远不会
达到,那么我想所有的东西都会被 GC 删除?
- 如果我通过
AtomicReference
将 CompletableFutures
中的一个交给外部,那么就有机会解锁 join()
,这里应该没有 GC,直到解锁发生了。但! join()
上的等待 myOuterThread
在 join()
之后不必再去那里了。因此,在 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 / finally 或 try with resources 来清除潜在的内存泄漏。
您需要做的就是确保您不会在变量、数据结构等中保留对各种未来、阶段等的引用,这些引用将在您的计算生命周期之后保持可访问/可达。如果你这样做...... 那些引用 可能是泄漏的来源。
线程泄漏不应该是您关心的问题。如果您的代码没有显式创建线程,它们将由执行程序服务/池管理。
此答案仅 解决了您“编辑 2”中的 3 个后续问题。
- 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
阶段,因此在 A
和 C
中的任何一个完成时完成。 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时,线程终止。到那时,它的本地局部变量(A
、B
、C
和D
)将无法访问。
- If I abort this by throwing an exception -> the
join()
is never reached, then i think all would be erased by the GC?
A
像以前一样完成,但 B
、C
和 D
没有。
但是,异常会终止线程,因此局部变量 A
、B
、C
和 D
将变得无法访问。
- If I give one of the
CompletableFuture
s 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 是否发生并不重要=]、C
和 D
。无论线程以何种方式终止,这些变量都变得不可访问。
但是,您现在已将对 CompletableFuture
对象之一的引用分配给一个与线程具有不同生命周期的变量。 可能 意味着 CompletableFuture
对象在线程终止后仍可访问。
如果我有一个(或多个)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^^
- 所以在正常情况下,在我的示例中,我没有外部
参考。线程中的CompletableFutures
从来没有机会 完成。通常 GC 可以安全地擦除线程 以及其中的内容CompetableFutures
。但我不 这么认为,这会发生吗? - 如果我通过抛出异常来放弃它 ->
join()
永远不会 达到,那么我想所有的东西都会被 GC 删除? - 如果我通过
AtomicReference
将CompletableFutures
中的一个交给外部,那么就有机会解锁join()
,这里应该没有 GC,直到解锁发生了。但!join()
上的等待myOuterThread
在join()
之后不必再去那里了。因此,在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 / finally 或 try with resources 来清除潜在的内存泄漏。
您需要做的就是确保您不会在变量、数据结构等中保留对各种未来、阶段等的引用,这些引用将在您的计算生命周期之后保持可访问/可达。如果你这样做...... 那些引用 可能是泄漏的来源。
线程泄漏不应该是您关心的问题。如果您的代码没有显式创建线程,它们将由执行程序服务/池管理。
此答案仅 解决了您“编辑 2”中的 3 个后续问题。
- 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
阶段,因此在 A
和 C
中的任何一个完成时完成。 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时,线程终止。到那时,它的本地局部变量(A
、B
、C
和D
)将无法访问。
- If I abort this by throwing an exception -> the
join()
is never reached, then i think all would be erased by the GC?
A
像以前一样完成,但 B
、C
和 D
没有。
但是,异常会终止线程,因此局部变量 A
、B
、C
和 D
将变得无法访问。
- If I give one of the
CompletableFuture
s to the outside by thatAtomicReference
, there then could be an chance to unblock thejoin()
.
三分:
AtomicReference
已分配B
,因此D
上的join()
不受影响。正如我们在上面看到的,
outsideReference.value()
上的假设join()
对于变量A
、[=14 是否发生并不重要=]、C
和D
。无论线程以何种方式终止,这些变量都变得不可访问。但是,您现在已将对
CompletableFuture
对象之一的引用分配给一个与线程具有不同生命周期的变量。 可能 意味着CompletableFuture
对象在线程终止后仍可访问。