简体   繁体   English

如何使 CompletableFuture 在完成后被垃圾收集回收?

[英]How to make a CompletableFuture be recycle by garbage collection when it's completed?

I built a task chain based on CompletableFuture of java, it could be very very long.我基于 java 的 CompletableFuture 构建了一个任务链,它可能会非常非常长。 My question is every task in CompletableFuture is an inner class UniCompletion , and it holds a reference to source CompletableFuture, so that it's impossible that completed CompletableFuture be garbage collected.我的问题是 CompletableFuture 中的每个任务都是内部 class UniCompletion ,并且它包含对源 CompletableFuture 的引用,因此完成的 CompletableFuture 不可能被垃圾收集。 Is there a way to avoid memory leaks for that?有没有办法避免 memory 泄漏?

Here is a piece of code that can be used to reproduce this error:下面是一段可用于重现此错误的代码:

    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);
    }

When I use the follow program,当我使用以下程序时,

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);
    }
}

it consistently prints on my machine它始终在我的机器上打印

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

This demonstrates that the future is reachable while its subsequent stage is evaluated, but not during the evaluation of the stage after next.这表明未来在其后续阶段被评估时是可达的,但在下一个阶段的评估期间不是。 It's not like the entire chain was kept referenced until the last stage finished.并不是整个链条在最后一个阶段完成之前一直被引用。

Only the first future stays reachable, which is unavoidable in the sequential evaluation of the chain, as everything happens in the complete method invoked from the main method on the first future.只有第一个未来保持可达,这在链的顺序评估中是不可避免的,因为一切都发生在从第一个未来的main方法调用的complete方法中。 When we change the program to当我们将程序更改为

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();
}

it prints它打印

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

on my machine, showing that the initial future can get garbage collected too when it is not referenced from a stack frame during the completion.在我的机器上,显示初始未来在完成期间未从堆栈帧中引用时也可以被垃圾收集。

The same applies when we use我们使用时也是如此

public static void main(String[] args) {
    CompletableFuture<Integer> f = CompletableFuture.supplyAsync(() -> 1);
    for(int i = 0; i < 6; i++) f = next(f);
    f.join();
}

regardless of whether the next method uses thenApply or thenApplyAsync .不管next方法是使用thenApply还是thenApplyAsync

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM