Hayami Kaito
← Apps

ガベージコレクションの可視化

参照カウント・マーク&スイープ・マーク&コンパクト・コピー GC・世代別 GC が、ヒープの上でゴミを見つけて回収する様子を 1 手ずつ見せます。循環参照や断片化、短命なオブジェクトの多いプログラムで方式ごとの違いを比べられます。

GC の方式

ルートからたどれるオブジェクトに印を付け(マーク)、印のないものを回収する(スイープ)方式。オブジェクトを動かさないので、回収した空きが細切れになっていきます(断片化)。

プログラム(台本)

リストを作り、途中で参照を外してゴミを作ってから GC を呼びます。どの方式でも D・E・F が回収されます。

プログラム

プログラム開始: ヒープは空

0122436xnullynullznull
  • 白(まだ届いていない)
  • 灰(届いた・未調査)
  • 黒(調べ終わった)
  • 転送済み(コピー GC)
GC の回数
0
GC の手数
0
調べた
0
動かした
0
回収した
0
空き(最大)
48(48)

方式の比べ方(「基本」を最後まで動かした結果)

「手数」はこのアプリで GC が 1 コマ進む処理の数で、プログラムが止まる時間の目安です。「1 回の最大」が小さいほど、1 回の停止が短くなります。

方式結果GC の回数手数の合計1 回の最大動かした回収した残ったゴミ
完了—32030
完了11212030
完了11010130
完了188430
完了2149612

使い方

  • GC の方式と、動かすプログラム(台本)を選んで「再生」を押します。どの方式でも同じ台本を動かすので、方式ごとの違いを比べられます。
  • マス目はメモリ(ヒープ)で、1 マスが 1 単位の大きさです。四角がオブジェクトで、下の小さな枠が参照のフィールドです。上の x・y・z はプログラムの変数(ルート)です。
  • 「◀」「▶」で 1 コマずつ戻る / 進む、「前の GC」「次の GC」で GC が始まるところへ移れます。
  • キーボードでは Space で再生 / 一時停止、← → で 1 コマ戻る / 進む、Shift + ← → で前 / 次の GC、Home / End で最初 / 最後へ移ります。

ガベージコレクションとは

プログラムが作ったオブジェクトのうち、変数(ルート)からたどって届かなくなったものは、もう二度と使われない「ゴミ」です。ガベージコレクション(GC)は、このゴミを自動で見つけてメモリを空け直すしくみです。JavaScript・Java・Go・Python など、多くの言語が GC を持っています。

三色マーキング

ルートからたどる GC(参照カウント以外)は、オブジェクトを 3 つの色に分けて考えます。白はまだ届いていないもの、灰は届いたが中身(指している先)をまだ調べていないもの、黒は調べ終わったものです。最初はすべて白で、ルートが指すものを灰にし、灰のものを 1 つずつ調べて黒にしていきます。灰がなくなったとき白のまま残っているものがゴミです。

方式ごとの得意・不得意

  • 参照カウント: ゴミになった瞬間に回収でき、長く止まることがありません。その代わり、参照を書き換えるたびに数を増減する手間がかかり、循環参照は回収できません(Python は循環参照のために別の GC も併用しています)。
  • マーク&スイープ: 実装が単純で、オブジェクトを動かしません。そのため空きが細切れになり(断片化)、空きの合計は足りていても大きなオブジェクトを置けなくなることがあります。
  • マーク&コンパクト: 生きているオブジェクトを詰めて断片化をなくします。動かす手間と、指している参照を書き換える手間がかかります。
  • コピー GC: 生きているオブジェクトだけを調べてコピーするので、ゴミが多いほど速く、断片化もしません。ただし使えるのはヒープの半分だけです。
  • 世代別 GC: 新しい世代だけを調べるマイナー GC を頻繁に行い、1 回の停止を短くします。古い世代のゴミは、たまに行うメジャー GC まで残ります。JavaScript の V8 や Java の HotSpot など、実用的な GC の多くがこの考え方を使っています。

このアプリでは、仕組みを見やすくするために単純化しています。世代別 GC では、マイナー GC で生き残ったオブジェクトをすぐに古い世代へ移し(実際には何回か生き残ってから移すことが多い)、古い世代はマーク&スイープで回収します。