CHIPLENS / INTERACTIVE

メモリは、いつ片づく?

GCとARCのコストの払い方

ARCは参照の増減に合わせて管理し、GCは不要な領域をまとめて回収する。違うのは、メモリを解放するタイミングとコストの分布です。

著者:shaf更新
01

触って確かめる

モデル(実測値ではありません)

同じ生成・寿命条件で、回収方法を変える。

メモリモデルの条件
GCの回収6 回
モデルの最大停止0.74 ms
ARCの参照操作684 回
メモリが減るタイミングを見る

共通の縦軸。GCは回収の帯、ARCは参照操作の点を表示。帯の幅・点の大きさは処理時間を表しません。

GCとARCの説明用メモリ使用量A / GC — 回収まで使用領域に残る050100B / ARC — 寿命の終了で解放0501000ms0ms400ms400ms800ms800ms1200ms1200ms
1ステップ = 20msの説明用時間

GCの回収6回、説明用の最大停止0.74ms。ARCの参照操作は684回。両方式共通の割当保留は0回。 0ms時点:GC 0、ARC 0モデル単位。

回収イベントを数値で見る
  • 300ms:54単位を回収、説明用停止0.74ms
  • 480ms:54単位を回収、説明用停止0.74ms
  • 660ms:54単位を回収、説明用停止0.74ms
  • 840ms:54単位を回収、説明用停止0.74ms
  • 1020ms:54単位を回収、説明用停止0.74ms
  • 1200ms:54単位を回収、説明用停止0.74ms

1オブジェクトを1モデル単位とし、短命は3ステップ、長命は12ステップです。短命個数は整数に丸めます。ARCは生成と最後の参照解放を各1操作として数え、CPU時間の優劣は計算しません。

短命なオブジェクトを増やしてみる

生成頻度を上げると、確保と解放の仕事が増えます。短命率を上げると、すぐ不要になるオブジェクトが多くなります。図のGC側では不要になっても回収まで領域が残り、回収時に下がります。ARC側は、設定した寿命が終わった時点で下がります。これは同じ生成列・寿命列を使った説明用モデルです。

ヒープを小さくすると、回収の間隔が変わります。ただし、生きているオブジェクト自体が上限に近づけば、どちらの方式も自由に割り当て続けることはできません。図では両方式の新規割当を同じステップで保留し、グラフの外へメモリを増やさないようにしています。

ARCは、最後の強い参照がなくなったら解放する

SwiftのARCはクラスのインスタンスへの参照を管理します。強い参照が残っている間はそのインスタンスを保持します。モデルでは参照の増減を小さな点と操作回数で表しましたが、実際のCPUコストは実装や最適化で変わります。ARCの仕組み(出典1)

解放するタイミングを参照の寿命から考えやすい一方で、解放処理の連鎖などもあるため、実機の停止時間まで一定とは限りません。また、互いを強く参照する循環が残れば解放されません。Swiftではweakやunownedなど、参照関係に合った設計が必要です。このモデルは循環参照を扱っていません。循環参照の説明(出典1)

現行のARTは、アプリをずっと止めて回収するわけではない

Android RuntimeのConcurrent Copying GCは、アプリのスレッドと並行してオブジェクトを移動します。公式資料は、短い停止と世代別回収について説明しています。GCという名前だけで、大きな停止が毎回起きると考えるのは正確ではありません。ARTのGC(出典2)

この図の帯は回収イベントの位置を見やすく示すもので、横幅を停止時間に比例させていません。表示する停止時間は「0.2ms + 回収モデル単位 × 0.01ms」という説明用の式です。ARTの実装式や特定端末の測定値ではなく、ARCとの速度ランキングにも使いません。

締切への影響は、フレーム描画で確かめる

回収や解放があっても、描画の締切に間に合えばその場面の表示更新は保たれます。反対に、メモリ管理以外の重い処理でも締切は超えます。フレーム描画とVSyncのモデルで、一時停止を加えたときの更新回数を確かめてください。

チップ比較のスコアだけからGC/ARCの影響やOS全体の優劣を決めず、実際のアプリ・端末での測定条件を確認するのが次の一歩です。

出典と取得日