RustはGCなしでどうやってヒープを管理しているかのメモ

September 24, 2017

Region-Based Memory Management in Cycloneを読んだ延長として、Rustにおけるヒープのメモリ管理についてメモ。

Cycloneではリージョンによるメモリ管理を導入しつつも、ヒープリージョンはGCによって管理されていた。一方でRustは完全にGCなしで安全にリークなしにヒープを扱えるようだ。

簡単にまとめると、Rustのドキュメントによるとまずスタック上で安全に扱える仕組みとして

  • 所有権
  • 参照・借用
  • ライフタイム

が紹介されている。ヒープ上にオブジェクトを作るBox<T>型は、RAIIを組み合わせてヒープの管理をスタック上の管理に結びつけることによって、ヒープでもどうように安全に扱えるようにしている。

このメモではメモリ安全で問題になる

  • 二重解放
  • 解放忘れ (リーク)
  • Dangling pointer

をRustはいかに防いでいるかを調べた。

所有権

所有権がどんなものかは Rustのドキュメントの所有権のところを参照。

所有”権”という名前だけれども権利というより義務に近く、変数がそのオブジェクトの解放義務を負わされている状態を「所有権」を持っている、という。

Rustではムーブセマンティクスを通して1つの変数にのみ所有権をもたせることにより、リソースの二重解放を防いでいる。

RAII

RAII(Resource Acquisition Is Initialization、日本語では「リソースの確保は初期化時に」、「リソースの取得と初期化」など)は、資源(リソース)の確保と解放を変数の初期化と破棄処理に結び付けるというプログラミングのテクニック、特にC++とD言語で一般的である。

RAIIでは資源の取得を変数の構築(初期化)時、返却を破壊時に行う。自動変数がスコープを離れるときデストラクタが呼ばれるため、変数の寿命が終わるとすぐに資源が返却されることが保障できるようになった。これは例外が起こったときでも同様であるため、RAIIは例外安全なコードを書くための鍵となる概念となった (Sutter 1999)。

https://ja.wikipedia.org/wiki/RAII

RustではBox<T> のコンストラクタ、デストラクタで malloc / free を呼んでいるイメージ。Box<T> 型の変数はスタック上にあり、所有権のルールに従って解放される。変数が解放されればデストラクタが呼ばれヒープ上のオブジェクトも解放される。

所有権があることによってデストラクトのタイミングを正確に把握できるというのもポイントである。 例えばSwiftは(4.0の時点では)所有権が導入されていないためstructについてdeinitを書くことができないが、Rustは所有権によってスタック上のオブジェクトが破棄されるタイミングを追える。

スタック上の仕組みに乗っかることにより、リークを防いでいる。

ライフタイム

Box型の変数が生きている限りはヒープ上のオブジェクトが解放されているということはないので基本的にはDangling pointerは起きない。

リージョンベースのメモリ管理によりDangling pointerを防げることはRegion-Based Memory Management in Cyclone で確認した。

Rustも参照について「ライフタイム」という名前ではあるがリージョンによる管理を行っているため、Box型の変数の参照についてもDangling pointerの問題は起こらない。

参照カウント

メモリリークが完全に起こらないかというとそんなことはない。

オブジェクトがスレッドをまたいだりする場合は、Arc 等を使って参照ウント方式でメモリ管理をせざるを得ない。この場合は循環参照によるメモリリークは発生しうる。

まとめ

Rustの所有権 / 借用 / ライフタイムの仕組みをRAIIによってヒープにも適用してメモリ管理を行っている。

このエントリーをはてなブックマークに追加