メモリの概要
Heap(ヒープ) | Stack(スタック) | Static(静的領域) | Text(コード) |
---|
- Text(コード)…アプリケーションをコンパイルすると機械語のバイナリコードが生成される。そのコードを保存する領域
- Static(静的領域)…const(定数)や文字リテラルの実体などを保存。
- Stack(スタック)…コード実行時に動的にメモリ領域を確保・解放。
- Heap(ヒープ)…コード実行時に動的にメモリ領域を確保・解放。メモリアクセスはスタックよりも遅くなる。
スタックメモリは、変数や配列など(あらかじめデータサイズがわかっているもの)をスタック上に積んでいき、解放する際は一番最後に積まれたものから解放する。最後にpushしたものが、一番最初にpopされる性質上、 取得する場所を探す必要がないことに加えて、スタック上のデータはすべてコンパイル時にサイズが決まっていないといけないため、高速に動作することができる。
push⇘ pop⇗
|スタック|
|スタック|
|スタック|
|スタック|
逆に、コンパイル時にサイズがわからないものや可変データはヒープメモリに格納する。ピープメモリは、スタックメモリに比べて低速で容量が大きい。ヒープにデータを格納する際、十分なサイズのメモリスペースを確保し、その場所のポインタを返す。十分なメモリスペースを確保したり、ポインタを追って目的の場所に行かないといけない等の理由から、低速になる。
※スタックに値を積むことをメモリ確保とは言わない。
コードのどの部分がピープ上のデータを使っているのか、ピープ上の重複するデータを最小化すること、メモリ不足にならないようにピープ上の未使用のデータを掃除することはすべて所有権が解決する問題である。
各言語のメモリ管理方法
メモリの管理において、あると望ましい特徴
- メモリをプログラマの任意のタイミングで解放すること
↳メモリ消費の管理 - 解放済みのオブジェクトへのポインタを使ってしまわないこと。
↳未定義動作につながる (クラッシュ、セキュリティーホール)
ポインタが存在するうちに値を開放するとポインタの参照先がなくなる
↳ダングリングポインタ
↳上記の条件を満たすことは困難か?
↳主要なプログラミング言語では、どちらかをある程度妥協して、メモリの制御を優先するか、安全性を重視するかをしている。
C, C++
…メモリの解放権限をプログラマに任せる。プログラマのミスがなければ、うまくいくが…
Python, JavaScript, Ruby, Java など
…ガベージコレクションを用いてメモリ管理を行う。オブジェクトに到達できるポインタがなくなったら、メモリを開放する。ガベージコレクタにメモリ解放の権限を渡してしまい、プログラマからは見えない。
⇒Rustはポインタの使い方に制約を加えることで、安全性の高いシステムを構築した。
所有者規則
- Rustの各値は所有者と呼ばれる変数と対応
- いかなる時も所有者は一人
- 所有者がスコープから外れたら所有されている値も一緒に削除される。
⇒ブロック内で宣言された変数はブロックを抜けたところで破棄される。 - 値を所有者から新しい所有者へ移動することができる。移送もとは所有者ではなくなる。(移動元は未初期化状態となる。)Rustでは、未初期化状態の変数は使うことができない。
- Copy型といわれる単純な型(整数、浮動小数点、文字など)は、所有権を持たない。
fn main() {
let x = 1; // ブロック内でxを初期化
} // xがスコープから抜ける。
fn main() {
let x = [1, 2, 3, 4, 5]; // xを初期化
let y = x; // yに所有権がわたる
for i in y {
println!("{}", i)
}
}
// print!("{}", y); // エラー yが存在しない
fn main() {
// Copy型には所有権はない
let x = 10;
let y = x;
println!("{}", x);
println!("{}", y);
}
>>>
10
10
参考文献
- プログラミングRustオーラリージャパン
- The Rust Programming Language 日本語版