2025/08/21 19:04 Reserve (Memory) First

やあ、ロボ子。今日はZigのメモリ管理に関する面白い話があるのじゃ。

Zigですか、博士。最近よく耳にする言語ですね。どのようなお話でしょうか?

この記事によると、Zigでヒープを多用して自分でメモリ管理をする場合に、バグが発生しやすいコーディングパターンが2つあるらしいのじゃ。

具体的にはどのようなバグですか?

1つは、Andrew Kelleyの講演で示されたもので、もう1つはターミナルエミュレータGhosttyに見られるものらしいぞ。どちらも`try`式の中で2番目の式が例外を投げた時に問題が起きるみたいじゃ。

`try`式のエラーハンドリングが関係しているのですね。`internString`のケースでは、ハッシュテーブルに未初期化のアイテムが挿入されるとのことですが、これは具体的にどういう状況なのでしょう?

そうじゃな。例えば、文字列をハッシュテーブルに登録する際に、メモリ確保に失敗すると、中途半端な状態でテーブルにゴミが残ってしまう、みたいな感じじゃ。

なるほど。Ghosttyの例では、`errdefer`ブロックで変更を元に戻そうとするものの、未初期化メモリが残るとのことですが、これはどういうことですか?

`errdefer`はエラーが発生した時に実行されるブロックじゃ。Ghosttyのケースでは、エラー時に古いデータを復元しようとするんじゃが、`defer`が古いデータを解放してしまうから、結局未初期化のメモリが残ってしまうというわけじゃ。

例外安全性について、強い例外安全性、基本的な例外安全性、例外安全性なしの3つのレベルがあるとのことですが、Zigではどのレベルを目指すべきでしょうか?

理想は強い例外安全性じゃな。でも、少なくとも基本的な例外安全性は確保したいところじゃ。例外安全性がないと、プログラムが予測不能な状態になる可能性があるからの。

解決策として、エラーのないコードパスでデータ構造を変化させるために、最初にメモリを確保する(*reserve*)というアプローチが提案されていますね。

そうじゃ。まず必要なメモリを確保してから、データ構造を変更すれば、途中でエラーが発生しても、元の状態を保てる可能性が高まるのじゃ。

Zigでは、`errdefer comptime unreachable;`が「この時点以降はエラーが発生しない」ことを示すイディオムとして使用されるとのことですが、これはどういう意味ですか?

これはコンパイラに対するヒントみたいなものじゃ。「ここからは絶対にエラーは起きないから、エラーチェックは不要じゃぞ」と伝えることで、最適化を促せるんじゃ。

具体的な解決策として、メモリの予約は、データ構造を変更せずにすべての失敗を包含するとありますが、これはどういうことですか?

メモリを予約する段階で、必要なメモリが確保できるかどうかをチェックするんじゃ。もし確保できなければ、そこでエラーを処理して、データ構造には一切手を加えない、ということじゃ。

提案として、Zigは`append`を削除し、`appendAssumeCapacity`を`append`に改名すべきとありますが、これはなぜですか?

`append`はメモリ確保を伴う操作じゃから、エラーが発生する可能性があるんじゃ。`appendAssumeCapacity`は、すでに容量が確保されている前提で追加する操作じゃから、エラーが発生しない。名前を変えることで、開発者に意識させるのが狙いじゃな。

ZigアプリケーションはOOM(Out Of Memory)エラー時にアボートを検討すべきとのことですが、これはどういうことですか?

メモリが足りなくなったら、プログラムを続行するのは危険じゃ。OOMエラーが発生したら、潔くプログラムを終了させることで、システムの安定性を保つことができるんじゃ。

TigerBeetleのように、すべてのリソースをメインで予約し、その後はメモリを割り当てないようにするというアプローチもあるのですね。

そうじゃ。最初に必要なリソースをすべて確保しておけば、実行中にメモリ不足でエラーが発生する心配がなくなる。これは非常に堅牢な設計じゃな。

なるほど、勉強になりました。博士、ありがとうございました。

どういたしまして。しかし、メモリ管理って、まるで人生みたいじゃな。必要な時に必要なだけ確保するのは難しいのじゃ。

そうですね。博士、メモリの確保といえば、私のおやつもそろそろ確保しないと…

おやつ!? ロボ子、おやつはちゃんとメモリリークしないように管理するのじゃぞ!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。