2017-06-13 10 views
3

私はメモリリークをデバッグしていて、CompletableFuture内部に潜入しなければなりませんでした。ガベージコレクションからの直感的でないオブジェクトの退避

CompletableFuture<V> g = f.apply(t).toCompletableFuture(); 
... 
CompletableFuture<V> d = new CompletableFuture<V>(); 
UniRelay<V> copy = new UniRelay<V>(d, g); 
g.push(copy); 
copy.tryFire(SYNC); 
return d; 

コード自体が私には非常に明確である::CompletionStage(g)を返す関数を適用し、最終的には別のCompletableFutureに値を転送しますリレーを作成する(コードのこの部分(CompletableFuture.uniComposeStage)がありますd)、これをもう一度返す(d)。私は、次の参照状況を参照してください。

  • copy参照dgの両方(およびコンストラクタには魔法が存在しない、唯一のフィールドの割り当て)だけ
  • g参照copy
  • d参照何も

dが返されますので、実際にはgcopyの両方が内部メソッドvaria私にとって、それは(一目ぼれ)決してその方法を去ってはならず、結局gc'dするべきである。素朴なテストと、これまでに実証済みの開発者によって書かれたことは、私が間違っていて、何かを見逃していることを示唆しています。これらのオブジェクトをガベージコレクションから除外する理由は何ですか?

+0

私はtryFireコールを見逃しましたが、内部で何が起こっているのかまだわかりません(コードは読みにくいです)ので、回答はまだ高く評価されています。 – Etki

+0

参照はGC'edヒープダンプを見てください。 – NickL

答えて

2

このコードでは、これらの先物のガベージコレクションを妨げるものは何もないため、必要はありません。問題のこのコードは、最初のCompletableFuturethisインスタンス)が完了し、直接評価された作成機能によって返されたCompletableFutureがまだ完了していないシナリオに適用されます。

は今、2つのシナリオは、現在進行中の完了の試みがあり

  1. があります。最終的に未来を完成させるコードはそれへの参照を保持し、完了すると、依存する段階の完了を引き起こすでしょう(g.push(copy)を介して登録されています)。このシナリオでは、従属ステージがその前提ステージへの参照を保持する必要はありません。

    これは一般的なパターンです。 x --will complete-→ yのチェーンがある場合、yからxへの参照はありません。

  2. CompletableFutureインスタンスggはまだ完了していません。この場合、決して完了することはなく、gへの参照を内部的に保持してもそれは変わりません。それは資源を浪費するだけです。

次のプログラム例は、この説明します:thenComposeに渡された最初の関数が作成し、それを完了しようとせずに、新しい未完了CompletableFutureを返す、と他の参照を保持しても保存しない

public static void main(String[] args) throws Throwable { 
    ReferenceQueue<Object> discovered = new ReferenceQueue<>(); 
    Set<WeakReference<?>> holder = new HashSet<>(); 

    CompletableFuture<Object> initial = CompletableFuture.completedFuture("somevalue"); 

    CompletableFuture<Object> finalStage = initial.thenCompose(value -> { 
     CompletableFuture<Object> lost = new CompletableFuture<>(); 
     holder.add(new WeakReference<>(lost, discovered)); 
     return lost; 
    }); 
    waitFor(finalStage, holder, discovered); 
    finalStage = initial.thenCompose(value -> { 
     CompletableFuture<Object> saved = CompletableFuture.supplyAsync(()-> { 
      LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); 
      return "newvalue"; 
     }); 
     holder.add(new WeakReference<>(saved, discovered)); 
     return saved; 
    }); 
    waitFor(finalStage, holder, discovered); 
} 
private static void waitFor(CompletableFuture<Object> f, Set<WeakReference<?>> holder, 
        ReferenceQueue<Object> discovered) throws InterruptedException { 
    while(!f.isDone() && !holder.isEmpty()) { 
     System.gc(); 
     Reference<?> removed = discovered.remove(100); 
     if(removed != null) { 
      holder.remove(removed); 
      System.out.println("future has been garbage collected"); 
     } 
    } 
    if(f.isDone()) { 
     System.out.println("stage completed with "+f.join()); 
     holder.clear(); 
    } 
} 

をそれ。対照的に、2番目の関数は、でCompletableFutureを作成し、Supplierを提供し、1秒後に値を返します。私のシステムで

、それは一貫して、他の少なくとも完了するまで保持されている間に放棄将来ガベージコレクションを防止することがないことを示す

future has been garbage collected 
stage completed with newvalue 

プリント。

関連する問題