プログラミング
ArenaAllocatorsはArrayListと相性が悪い
ArenaAllocators don't play nicely with ArrayLists (openmymind.net)
要約
ArenaAllocatorは、メモリを解放しても即座にアリーナに返却されるとは限らないため、ArrayListのような動的にサイズが変更されるデータ構造と組み合わせると、メモリ使用量が想定以上に増加する可能性があります。これは、ArrayListの拡張時に古いメモリが解放されるタイミングが、ArenaAllocatorの解放条件を満たさない場合に発生します。
全文翻訳
ArenaAllocatorsはArrayListと相性が悪い
Aug 04, 2026
以前、ArenaAllocatorsと、なぜそのfreeをnoopとして扱うべきかについて検討しました。要点は、freeまたはdestroyが実際にメモリをアリーナに返却するためには、2つの条件を満たす必要があるということです:
解放されたメモリは、最後に割り当てられたメモリである必要がある。
解放されたメモリは、Arenaの現在のノードから割り当てられたものでなければならない(これはArenaがどのように成長するかという内部的な詳細です)。
したがって、このコードを考えると:
var a = try arena.alloc(u8, 100);
var b = try arena.alloc(u8, 100);
arena.free(b);
arena.free(a);
bのメモリはアリーナに返却されると確信できますが、aのメモリについては何も言えません。なぜでしょうか?bは最後に割り当てられたメモリであったため解放され、定義上、アリーナの現在のノードから取得されたものでなければなりません。bが解放された後、aは最初の要件を満たします。それは最後に割り当てられたメモリになります。しかし、bの割り当てが新しいノードを作成したかどうかはわかりません。アリーナの現在のノードは、aが取得されたノードである場合もあれば、そうでない場合もあります。
ノードの件が混乱するように思えるなら、元の投稿を読むか、単に忘れてください。なぜなら、最初の単純なルールが満たされない一般的な状況があるからです。それはArrayList(またはWriter.Allocating)の成長です。
データがArrayListに追加されると、まず既存のメモリを再マッピングしようとします(つまり、インプレースで成長させます)。しかし、それが失敗した場合、より大きな新しいチャンクを割り当て、メモリをコピーし、以前の割り当てを解放します。問題がわかりますか?
以下はarray_list.zigからのコードです:
const new_memory = try gpa.alignedAlloc(T, alignment, new_capacity);
@memcpy(new_memory[0..self.items.len], self.items);
gpa.free(old_memory);
ArrayListの成長と他の割り当てをインターリーブしていない場合でも、割り当て+コピー+解放は、old_memoryが最後の割り当てではないことを保証します(最後の割り当てはnew_memoryです)。
解決策はありますか?実際にはありませんが、2つのことができます。第一に、ArrayListのサイズを適切に設定します(例: initCapacityまたはensureTotalCapacityPreciseを使用)。第二に、追加/書き込みと他の割り当てをインターリーブすることを避けます。もしそれができれば、リマップブランチに遭遇する可能性がはるかに高くなり、この問題を回避できます。
どちらも、どのような種類のAllocatorを使用しているかに関わらず、念頭に置くべき良い点ですが、ArenaAllocatorを使用する場合、最悪のケースは約3倍のメモリになります。
これは明白だとわかっていますが、実際に考えたことはありませんでした。おそらく私だけではないでしょう。