java - 如何在 CompletableFuture 完成后通过垃圾收集进行回收?
问题描述
我基于 java 的 CompletableFuture 构建了一个任务链,它可能非常非常长。我的问题是 CompletableFuture 中的每个任务都是一个内部类UniCompletion
,并且它包含对源 CompletableFuture 的引用,因此完成的 CompletableFuture 不可能被垃圾收集。有没有办法避免内存泄漏?
这是一段可用于重现此错误的代码:
public static void main(String... args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
AtomicReference<CompletableFuture<Integer>> future = new AtomicReference<>(CompletableFuture.completedFuture(0));
IntStream.range(0, 100000000).forEach(i -> future.set(future.get().thenApplyAsync(ii -> ii + 1, executor)));
future.get().get();
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
}
解决方案
当我使用以下程序时,
import java.lang.ref.Cleaner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.LockSupport;
public class CfGc {
static final Cleaner CLEANER = Cleaner.create();
static CompletableFuture<Integer> next(CompletableFuture<Integer> f) {
Object[] status = { "not completed" };
CLEANER.register(f, () -> System.out.println(status[0]+" future collected"));
return f.whenComplete((i,t) -> {
status[0] = t != null? t: i;
LockSupport.parkNanos(500_000_000);
System.out.println(status[0]+" completed, running gc()");
System.gc();
LockSupport.parkNanos(5_000_000);
System.out.println(status[0]+" completed, gc() ran\n");
}).thenApply(i -> i + 1);
}
public static void main(String[] args) {
CompletableFuture<Integer> s = new CompletableFuture<>(), f = s;
for(int i = 0; i < 6; i++) f = next(f);
s.complete(1);
}
}
它始终在我的机器上打印
1 completed, running gc()
1 completed, gc() ran
2 completed, running gc()
2 completed, gc() ran
3 completed, running gc()
2 future collected
3 completed, gc() ran
4 completed, running gc()
3 future collected
4 completed, gc() ran
5 completed, running gc()
4 future collected
5 completed, gc() ran
6 completed, running gc()
5 future collected
6 completed, gc() ran
这表明,在评估其后续阶段时,未来是可到达的,但在下一阶段的评估期间却不是。在最后一个阶段完成之前,并不是整个链都被引用。
只有第一个未来保持可达,这在链的顺序评估中是不可避免的,因为一切都发生在从第一个未来的complete
方法调用的方法中。main
当我们将程序更改为
static final Cleaner CLEANER = Cleaner.create();
static CompletableFuture<Integer> next(CompletableFuture<Integer> f) {
Object[] status = { "not completed" };
CLEANER.register(f, () -> System.out.println(status[0]+" future collected"));
return f.whenComplete((i,t) -> {
status[0] = t != null? t: i;
LockSupport.parkNanos(500_000_000);
System.out.println(status[0]+" completed, running gc()");
System.gc();
LockSupport.parkNanos(5_000_000);
System.out.println(status[0]+" completed, gc() ran\n");
}).thenApplyAsync(i -> i + 1);
}
public static void main(String[] args) {
CompletableFuture<Integer> s = new CompletableFuture<>(), f = s;
for(int i = 0; i < 6; i++) f = next(f);
s.complete(1);
s = null;
f.join();
}
它打印
1 completed, running gc()
1 completed, gc() ran
2 completed, running gc()
1 future collected
2 completed, gc() ran
3 completed, running gc()
2 future collected
3 completed, gc() ran
4 completed, running gc()
3 future collected
4 completed, gc() ran
5 completed, running gc()
4 future collected
5 completed, gc() ran
6 completed, running gc()
5 future collected
6 completed, gc() ran
在我的机器上,表明最初的未来在完成期间没有从堆栈框架中引用时也可以收集垃圾。
当我们使用时同样适用
public static void main(String[] args) {
CompletableFuture<Integer> f = CompletableFuture.supplyAsync(() -> 1);
for(int i = 0; i < 6; i++) f = next(f);
f.join();
}
无论该next
方法是否使用thenApply
或thenApplyAsync
。
推荐阅读
- python - 如何对 boto3 lambda 客户端调用函数进行单元测试
- dialogflow-es - 如何在 Dialogflow 上实现 Permission API
- php - php导出mysql表到excel并创建本地文件,不下载
- python-3.x - 熊猫数据框中两个给定日期之间的日期列表
- c# - 带有 .include .join 和 .where 的 sql linq lambda 表达式
- python - python - 如何从python中numpy.searchsorted的结果加快数组屏蔽的性能?
- python - 无法修复'RuntimeWarning:协程'Command.__call__'从未等待'错误
- c++ - 删除整个链表
- database - 在未连接到同一网络的计算机上复制 oracle 数据库
- html - 删除 CSS 网格中不需要的空间