簡體   English   中英

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

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

我基於 java 的 CompletableFuture 構建了一個任務鏈,它可能會非常非常長。 我的問題是 CompletableFuture 中的每個任務都是內部 class UniCompletion ,並且它包含對源 CompletableFuture 的引用,因此完成的 CompletableFuture 不可能被垃圾收集。 有沒有辦法避免 memory 泄漏?

下面是一段可用於重現此錯誤的代碼:

    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

這表明未來在其后續階段被評估時是可達的,但在下一個階段的評估期間不是。 並不是整個鏈條在最后一個階段完成之前一直被引用。

只有第一個未來保持可達,這在鏈的順序評估中是不可避免的,因為一切都發生在從第一個未來的main方法調用的complete方法中。 當我們將程序更改為

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM