Zen標準ライブラリで提供されているアロケータを紹介し、その選択方法について説明します。各アロケータは初期化方法が異なりますが、基本的な利用方法は同じです。ただし、ArenaAllocator
に関してはアリーナのメモリを全て解放するという操作が可能です。
まず、libcをリンクする場合、C言語のアロケータをラッピングしたstd.heap.c_allocator
が有力候補です。このアロケータはlibc
のrealloc
を呼ぶことでメモリを管理しています。c_allocator
はグローバルなインスタンスが用意されているため、libcさえリンクされていれば、初期化なしで動きます。
examples/ch08-memory/src/allocators.zen:7:11
test "c_allocator" {
var allocator = heap.c_allocator;
var allocated = try heap.create(allocator, u32);
defer heap.destroy(allocator, allocated);
}
固定長のバッファによるアロケーションで十分な場合、std.heap.FixedBufferAllocator
もしくはstd.heap.ThreadSafeFixedBufferAllocator
が利用できます。ベアメタル / 組込みシステムで固定長のバッファをアロケータで利用する場合にも、これらのアロケータは有用です。これらのアロケータには、[]mut u8
のスライスを.buffer
フィールドに渡します。
examples/ch08-memory/src/allocators.zen:13:18
test "FixedBufferAllocator" {
var buffer: [100]u8 = undefined;
var allocator = heap.FixedBufferAllocator{ .buffer = &mut buffer };
var allocated = try heap.create(&mut allocator, u32);
defer heap.destroy(&mut allocator, allocated);
}
page_allocator
は、OS APIを使って (WindowsではVirtualAlloc
、それ以外ではmmap
) メモリを確保します。page_allocator
はグローバルなインスタンスが用意されているため、(Linux / Windows / macOS上で動くアプリケーションであれば) 初期化なしで動きます。
examples/ch08-memory/src/allocators.zen:20:24
test "page_allocator" {
var allocator = heap.page_allocator;
var allocated = try heap.create(allocator, u32);
defer heap.destroy(allocator, allocated);
}
ArenaAllocator
は短命のコマンドラインアプリケーションや、周期的なパターンのあるアプリケーションで効力を発揮するでしょう。このアロケータは、割り当てメモリ領域を記憶し、deinit
により確保したメモリを全て解放します。ArenaAllocator
は他のアロケータのラッパーアロケータとして動作します。そのため、初期化では他のアロケータをallocator
フィールドに渡します。
examples/ch08-memory/src/allocators.zen:26:30
test "ArenaAllocator" {
var arena = heap.ArenaAllocator{ .allocator = heap.c_allocator };
defer arena.deinit();
var allocated = try heap.create(&mut arena, u32);
}
error.OutOfMemory
発生時のエラー処理をテストしたい場合、std.heap.FailingAllocator
を利用して下さい。FailingAllocator
は N 回メモリを割り当てた後、次のメモリ割り当てでerror.OutOfMemory
を返します。アロケータの初期化では、fail_index
とinternal_allocator
を渡します。fail_index
に N を指定します。
examples/ch08-memory/src/allocators.zen:32:42
test "FailingAllocator" {
var allocator = std.heap.FailingAllocator{
.fail_index = 2,
.internal_allocator = heap.c_allocator,
};
_ = try heap.create(&mut allocator, u32);
_ = try heap.create(&mut allocator, u32);
// 2回メモリを割り当てた後、次の割り当ては失敗する
const result = heap.create(&mut allocator, u32);
err(error.OutOfMemory, result);
}
最後に、WebAssemblyをビルドする場合、std.heap.page_allocator
を利用して下さい。ターゲットがWebAssemblyのときにはビルド時に page_allocator
内で自動的にWebAssembly用のアロケータが選択されます。
上記選択肢に当てはまらない場合、新たなアロケータを実装することが可能です。
新たなアロケータを実装するためには、std.heap.Allocator
インタフェースを実装します。このインタフェースは2つのメソッドrealloc
とshrink
の実装を要求します。
realloc
はメモリの再割当て / アライメントの変更、を行うための関数で、メモリを新たに割り当てる場合にも利用されます。shrink
は確保済みメモリを縮小 / アライメントを変更、するための関数で、メモリを解放する場合にも利用されます。
pub const Allocator = interface {
pub const Error = error{OutOfMemory};
pub fn realloc(
old_mem: []mut u8,
old_alignment: u29,
new_byte_count: usize,
new_alignment: u29,
) Error![]mut u8;
pub fn shrink(
old_mem: []mut u8,
old_alignment: u29,
new_byte_count: usize,
new_alignment: u29,
) []mut u8;
};
具体的なアロケータを使用せず、Allocator
インタフェースを引数として受け取るAPIを設計しましょう。そのようにすることで、ライブラリのユーザが利用するアロケータを決定することができます。
☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.