Heap とは?寿命の長いデータのためのメモリ
heap は、サイズや寿命が事前に分からないデータのためのメモリ領域で、必要に応じて確保され、手動または garbage collector によって解放されます。
データがそれを作った関数より長く生きる必要があるとき、あるいはサイズが runtime で決まるとき、それは stack ではなく heap に置かれます。heap の確保は stack より柔軟ですが遅く、そのメモリは関数が return しても自動的には回収されません。あなたが解放するか、garbage collector が解放します。
heap は何のためにあるか
heap は、サイズが動的、あるいは寿命が多くの関数呼び出しにまたがるオブジェクト、配列、構造体を保持します。ブロックを要求し、必要な限り使い、終わったら直接または garbage collection 経由で解放します。
heap と stack
stack は高速で固定サイズ、return 時に自動クリーンアップされます。heap は柔軟で大きく、管理が必要です。stack のデータは関数とともに消えますが、heap のデータは解放または回収されるまで生き続けます。だからこそ寿命の長い状態は heap に属します。
heap メモリの管理方法
- Manual: C の malloc と free のように、明示的に確保と解放を行います。
- Garbage-collected: runtime が到達不能なオブジェクトを自動的に回収します。
- Reference-counted: 最後の参照がなくなったときにメモリが解放されます。
- Pool または arena: ブロックをまとめてグループ化し、速度のために一括で解放します。
注意すべき heap の問題
heap のメモリは自動的に解放されないため、leak が蓄積し、時間とともに断片化が確保を遅くする場所です。また、枯渇して out-of-memory 状態を引き起こす可能性が最も高いメモリでもあります。
簡単な例
関数から return して別の場所で使い続ける大きなリストを作ると、そのデータは heap に存在します。stack から消えてしまうローカルとは違い、関数が return した後も残り続けます。
CI における heap
heap の使用こそが CI のほとんどの out-of-memory 失敗の中身です。build や test run が runner の許す以上の heap を確保して kill されます。より大きな runner(Latchkey)は上限を引き上げ、一時的な OOM kill は自動 retry できますが、本物の leak はやはり修正が必要です。
重要なポイント
- heap は動的なサイズで寿命の長いデータを、必要に応じて確保して格納します。
- 柔軟ですが stack より遅く、関数の return で自動解放されません。
- heap の枯渇は、CI のほとんどの out-of-memory 失敗の背後にあります。