8

永続コレクションの一部への参照を保持している場合、コレクション全体をガベージ コレクションできますか? これを正しく理解していますか?

関数 gctest は、コレクションの動作をテストするためのものです。

(defn gctest
  "A shot about testing the gc-ability of persistent thingies."
  [n]
  (take 5 (drop 100 (vec (range n)))))

main=> (def a (gctest 1e7))
main=> (def b (gctest 1e7))
main=> (def c (gctest 1e7))
main=> (def d (gctest 1e7))
main=> (def e (gctest 1e7))
main=> (def f (gctest 1e7))
main=> (def g (gctest 1e7))

OutOfMemoryError GC overhead limit exceeded  clojure.lang.ArrayChunk.dropFirst     (ArrayChunk.java:54)

頭の保持についてはすでに聞いたことがありますが、これはもう少し一般的ですか?

私が理解したいのは、大きく変化するコレクションをどのように使用できるかということです。コレクションの大部分は時間の経過とともに変化すると予想されます。原則として、大部分がガベージ コレクションになる可能性がありますが、すべてではありません。

これに対処する標準的な方法はありますか?

4

2 に答える 2

7

標準の GC ルールはそのままです。コレクションの一部への参照を保持している限り、参照からアクセスできるすべてのオブジェクトはメモリ内にとどまります。したがって、参照からアクセスできるコレクションの部分のみがholdになり、残りは収集されます。特に、100 個の要素リストの最後の 50 個の要素を参照すると、最初の 50 個の要素が収集され、残りはメモリに残ります。

ただし、あなたの場合、100番目から始まる各コレクションのすべての要素が保持されます。その理由は遅延評価です。関数は、(あなたの場合) 5 つの要素の遅延takeシーケンスを生成します。レイジー シーケンス オブジェクト自体は実際のシーケンスではなく、特別なジェネレーター オブジェクトです (ただし、Clojure の用語ではなく、Python の用語です)。遅延シーケンスの要素が必要な場合は、generator オブジェクトが生成して返します。ただし、要素を要求しない場合、ジェネレーターは、要素を生成するために必要なすべてのオブジェクトへの参照を保持するだけです。

あなたの例では、大きなベクトルを作成し、そこから 5 つの要素を要求し、結果を変数abcなどに保存します。Clojure は、100 番目の要素を指す大きなベクトルとジェネレータ オブジェクトを作成します。コレクション自体への参照は失われますが、ジェネレータ オブジェクトへの参照は最上位に保存されます。ジェネレーター オブジェクトを評価しないため、実際の 5 要素シーケンスを作成することはありません。REPL は vars abcなどを参照し、これらの vars はジェネレーター オブジェクトを参照し、ジェネレーター オブジェクトは実際の 5 つの要素シーケンスを生成するために必要なコレクションを参照します。したがって、すべてのコレクションのすべての要素 (最初の 100 個の要素を除く) をメモリに保持する必要があります。

一方、ジェネレーター オブジェクトを評価すると、実際の 5 つの要素シーケンスが生成され、残りのコレクションへの参照が忘れられます。これを試して:

user> (def a (gctest 1e7))
#'user/a                                                                                                                                               
user> (println a)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def b (gctest 1e7))
#'user/b                                                                                                                                               
user> (println b)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def c (gctest 1e7))
#'user/c                                                                                                                                               
user> (println c)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def d (gctest 1e7))
#'user/d                                                                                                                                               
user> (println d)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def e (gctest 1e7))
#'user/e                                                                                                                                               
user> (println e)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def f (gctest 1e7))
#'user/f                                                                                                                                               
user> (println f)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def g (gctest 1e7))
#'user/g                                                                                                                                               
user> (println g)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def h (gctest 1e7))
#'user/h                                                                                                                                               
user> (println h)
(100 101 102 103 104)                                                                                                                                  
nil                                                                                                                                                    
user> (def i (gctest 1e7))
#'user/i                                                                                                                                               
user> (println i)
(100 101 102 103 104)                                                                                                                                  
nil 

OutOfMemory はありません。Vars abcなどは 5 つの要素の実際のリストを格納するようになったため、大きなコレクションへの参照がなくなり、収集できるようになりました。

于 2012-06-14T18:50:20.627 に答える
4

この種の問題は、遅延シーケンスを使用して解決できます。あなたの場合、関数vecによって生成された各要素を通過することにより、実際にメモリ内にベクトルを作成する関数を使用しましたrange(範囲は遅延シーケンスを返します)。

以下のコード(vec呼び出しなしではメモリの問題はありません)

(defn gctest
  "A shot about testing the gc-ability of persistent thingies."
  [n]
  (take 5 (drop 100 (range n))))

更新

呼び出しを使用するvecと、メモリ内のすべての要素が保持され、GC によって収集されません。これらの要素は、関数から返されたシーケンス オブジェクトによって参照 (および必要) され、gctest必要な要素を取得できるためです (つまり、100 個の要素をスキップします)。シーケンスオブジェクトが要素を要求されたときに、5 つの要素を取得します)。

于 2012-06-14T16:43:29.533 に答える