プログラム メモリの見直し
プログラムは、次の 2 つの方法でメモリを割り当てます。
-
スタック: ローカル変数用の連続したメモリ領域。
- 値のサイズは固定されており、コンパイル時に判明しています。
- 非常に高速: スタック ポインタを移動するだけです。
- 関数呼び出しによって行われるため、管理が容易です。
- メモリ局所性に優れています。
-
ヒープ: 関数呼び出しに依存しない値の保持領域。
- 値のサイズは動的で、実行時に決定されます。
- スタックよりやや低速で、何らかののブックキーピングが必要です。
- メモリの局所性が保証されません。
例
String
を作成すると、スタックには固定サイズのメタデータが配置され、ヒープにはサイズが動的に決定されるデータ(実際の文字列)が配置されます。
fn main() { let s1 = String::from("Hello"); }
This slide should take about 5 minutes.
-
String
はVec
により実現されているため、容量と長さがあり、可変であればヒープ上の再割り当てによって拡張できることを説明します。 -
受講者から尋ねられた場合は、システム アロケータを使用してメモリ領域がヒープから割り当てられること、Allocator API を使用してカスタム アロケータを実装できることを説明してください。
その他
unsafe
Rust を使用してメモリ レイアウトを調べることが出来ます。ただし、これは当然ながら安全でないことを指摘する必要があります。
fn main() { let mut s1 = String::from("Hello"); s1.push(' '); s1.push_str("world"); // 自宅では行わないでください。これは説明のみを目的としています。 // String はそのレイアウトを保証しないため、未定義の動作が // 発生する可能性があります。 unsafe { let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1); println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}"); } }