32

GCプロセスで実際に何が起こっているのかをより深く知るために、Javaのガベージコレクションに関する資料を読んでいます。

「カードテーブル」という仕組みに出会いました。私はそれをグーグルで検索しましたが、包括的な情報は見つかりませんでした. 説明のほとんどはかなり浅く、魔法のように説明しています。

私の質問は次のとおりです: カード テーブルと書き込みバリアはどのように機能しますか? カードテーブルには何がマークされていますか? ガベージ コレクターは、特定のオブジェクトが古い世代で永続化された別のオブジェクトによって参照されていることをどのように認識しますか。

シミュレーションを準備することになっていたように、そのメカニズムについて実際の想像力を持ちたいと思います。

4

3 に答える 3

42

非常に悪い説明を見つけたのか、それとも詳細を期待しすぎたのかはわかりませんが、私が見た説明には非常に満足しています。説明が簡潔で単純に聞こえる場合、それは実際にはかなり単純なメカニズムだからです。

すでにご存知のように、世代別ガベージ コレクターは、若いオブジェクトを参照する古いオブジェクトを列挙できる必要があります。すべての古いオブジェクトをスキャンするのは正しいことですが、それでは世代別アプローチの利点が失われるため、対象を絞り込む必要があります。その方法に関係なく、書き込みバリアが必要です。これは、(参照型の) メンバー変数が割り当てられたり書き込まれたりするたびに実行されるコードです。新しい参照が若いオブジェクトを指し、それが古いオブジェクトに格納されている場合、書き込みバリアはその事実をガベージ コレクションに記録します。違いは、その記録方法にあります。若いオブジェクトへの参照を持っている(ある時点で持っていた)すべての古いオブジェクトのコレクションである、いわゆる記憶セットを使用する正確なスキームがあります。ご想像のとおり、これにはかなりのスペースが必要です。

カード テーブルにはトレードオフがあります。どのオブジェクトに若いポインターが正確に含まれているか (または、少なくともある時点で含まれていたか) を伝える代わりに、オブジェクトを固定サイズのバケットにグループ化し、どのバケットに若いポインターを持つオブジェクトが含まれているかを追跡します。もちろん、これによりスペースの使用量が削減されます。正確を期すために、一貫性がある限り、オブジェクトをどのようにバケット化するかは問題ではありません。効率を高めるために、メモリアドレスでグループ化し(無料で利用できるため)、より大きな2の累乗で割って(除算を安価なビット単位の操作にするため)、それらをグループ化します。

また、バケットの明示的なリストを維持する代わりに、可能なバケットごとに事前にスペースを予約します。具体的には、N ビットまたはバイトの配列があり、N はバケットの数であるためi、th バケットに若いポインターが含まれていない場合は 0 になり、i若いポインターが含まれている場合は 1 になります。これがカードテーブル本体です。通常、この領域は、ヒープ (の一部) として使用される大きなメモリ ブロックと共に割り当てられ、解放されます。拡張する必要がない場合は、メモリ ブロックの先頭に埋め込むこともできます。アドレス空間全体がヒープとして使用されていない限り (これは非常にまれです)、上記の式はstart_of_memory_region >> K0 ではなく 0 から始まる数値を与えるため、カード テーブルへのインデックスを取得するには、ヒープの開始アドレスの開始を減算する必要があります。

要約すると、書き込みバリアは、ステートメントsome_obj.field = other_obj;が古いオブジェクトに若いポインターを格納していることを検出すると、次のことを行います。

card_table[(&old_obj - start_of_heap) >> K] = 1;

は、現在若いポインター&old_objを持つオブジェクトのアドレスです (古いオブジェクトを参照することが決定されたため、既にレジスターにあります)。マイナー GC 中に、ガベージ コレクターはカード テーブルを調べて、若いポインターをスキャンするヒープ領域を決定します。

for i from 0 to (heap_size >> K):
    if card_table[i]:
        scan heap[i << K .. (i + 1) << K] for young pointers
于 2013-10-03T09:22:57.287 に答える
20

しばらく前に、HotSpot JVM の若いコレクションのメカニズムを説明する記事を書きました。 HotSpot のマイナー GC である JVM での GC の一時停止について

ダーティ カード ライト バリアの原理は非常に単純です。プログラムがメモリ内の参照を変更するたびに、変更されたメモリ ページをダーティとしてマークする必要があります。JVM には特別なカード テーブルがあり、メモリの各 512 バイト ページには、カード テーブル内の 1 バイトのエントリが関連付けられています。

通常、古い空間から若い空間へのすべての参照を収集するには、古い空間内のすべてのオブジェクトをスキャンする必要があります。そのため、書き込みバリアが必要です。若い空間内のすべてのオブジェクトは、書き込みバリアの最後のリセット以降に作成 (または再配置) されているため、ダーティでないページは若い空間への参照を持つことができません。これは、ダーティ ページ内のオブジェクトのみをスキャンできることを意味します。

于 2013-10-04T04:25:17.723 に答える