# 自定義內存分配器
> [custom-allocators.md](https://github.com/rust-lang/rust/blob/master/src/doc/book/custom-allocators.md)
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4
分配內存并不總是最簡單的事情,同時通常 Rust 默認會負責它,不過經常自定義內存分配會變得必要。編譯器和標準庫目前允許在編譯時切換目前默認使用的全局分配器。設計目前稱作[RFC 1183](https://github.com/rust-lang/rfcs/blob/master/text/1183-swap-out-jemalloc.md)不過這里我們會教你如何獲取你自己的分配器并運行起來。
### 默認分配器
編譯器目前自帶兩個默認分配器:`alloc_system`和`alloc_jemalloc`(然而一些目標平臺并沒有 jemalloc)。這些分配器是正常的 Rust crate 并包含分配和釋放內存的 routine 的實現。標準庫并不假設使用任何一個編譯,而且編譯器會在編譯時根據被產生的輸出類型決定使用哪個分配器。
編譯器產生的二進制文件默認會使用`alloc_jemalloc`(如果可用的話)。在這種情況下編譯器“控制了一切”,從它超過了最終鏈接的權利的角度來看。大體上這意味著分配器選擇可以被交給編譯器。
動態和靜態庫,然而,默認使用`alloc_system`。這里 Rust 通常是其他程序的“客人”或者處于并沒有權決定應使用的分配器的世界。為此它求助于標準 API(例如,`malloc`和`free`)來獲取和釋放內存。
### 切換分配器
雖然編譯器默認的選擇大部分情況工作良好,也經常需要定制特定的方面。覆蓋編譯器關于使用哪個分配器的選擇可以簡單的通過鏈接到期望的分配器實現:
~~~
#![feature(alloc_system)]
extern crate alloc_system;
fn main() {
let a = Box::new(4); // allocates from the system allocator
println!("{}", a);
}
~~~
在這個例子中生成的二進制文件并不會默認鏈接到 jemalloc 而是使用了系統分配器。同理生成一個默認使用 jemalloc 的動態庫可以寫成:
~~~
#![feature(alloc_jemalloc)]
#![crate_type = "dylib"]
extern crate alloc_jemalloc;
pub fn foo() {
let a = Box::new(4); // allocates from jemalloc
println!("{}", a);
}
# fn main() {}
~~~
### 編寫一個自定義分配器
有時甚至 jemalloc 與系統分配器之間的選擇都是不夠的并需要一個新的自定義的分配器。這種情況你要編寫你自己實現了分配器 API(例如與`alloc_system`和`alloc_jemallo`相同)的 crate。作為一個例子,讓我們看看一個簡單的和聲明化的`alloc_system`版本:
~~~
# // only needed for rustdoc --test down below
# #![feature(lang_items)]
// The compiler needs to be instructed that this crate is an allocator in order
// to realize that when this is linked in another allocator like jemalloc should
// not be linked in
#![feature(allocator)]
#![allocator]
// Allocators are not allowed to depend on the standard library which in turn
// requires an allocator in order to avoid circular dependencies. This crate,
// however, can use all of libcore.
#![no_std]
// Let's give a unique name to our custom allocator
#![crate_name = "my_allocator"]
#![crate_type = "rlib"]
// Our system allocator will use the in-tree libc crate for FFI bindings. Note
// that currently the external (crates.io) libc cannot be used because it links
// to the standard library (e.g. `#![no_std]` isn't stable yet), so that's why
// this specifically requires the in-tree version.
#![feature(libc)]
extern crate libc;
// Listed below are the five allocation functions currently required by custom
// allocators. Their signatures and symbol names are not currently typechecked
// by the compiler, but this is a future extension and are required to match
// what is found below.
//
// Note that the standard `malloc` and `realloc` functions do not provide a way
// to communicate alignment so this implementation would need to be improved
// with respect to alignment in that aspect.
#[no_mangle]
pub extern fn __rust_allocate(size: usize, _align: usize) -> *mut u8 {
unsafe { libc::malloc(size as libc::size_t) as *mut u8 }
}
#[no_mangle]
pub extern fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) {
unsafe { libc::free(ptr as *mut libc::c_void) }
}
#[no_mangle]
pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize,
_align: usize) -> *mut u8 {
unsafe {
libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8
}
}
#[no_mangle]
pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, old_size: usize,
_size: usize, _align: usize) -> usize {
old_size // this api is not supported by libc
}
#[no_mangle]
pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize {
size
}
# // just needed to get rustdoc to test this
# fn main() {}
# #[lang = "panic_fmt"] fn panic_fmt() {}
# #[lang = "eh_personality"] fn eh_personality() {}
# #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
~~~
在我們編譯了這個 crate 之后,他可以被如下使用:
~~~
extern crate my_allocator;
fn main() {
let a = Box::new(8); // allocates memory via our custom allocator crate
println!("{}", a);
}
~~~
### 自定義分配器的限制
使用自定義分配器時要滿足一些限制,否則可能導致編譯器錯誤:
-
任何一個程序只能鏈接到一個分配器。二進制、動態庫和靜態庫必須正好鏈接到一個分配器上,并且,如果沒有顯式選擇,編譯器會選擇一個。另一方面,rlib 并不需要鏈接到一個分配器(不過仍然可以)。
-
一個標記為`#![needs_allocator]`(例如,目前的`liballoc`)的分配器使用者和一個`#[allocator]` crate 不能直接依賴一個需要分配器的 crate(例如,循環引用是不允許的)。這基本上意味著目前分配器必須依賴 libcore。
- 前言
- 貢獻者
- 1.介紹
- 2.準備
- 3.學習 Rust
- 3.1.猜猜看
- 3.2.哲學家就餐問題
- 3.3.其它語言中的 Rust
- 4.語法和語義
- 4.1.變量綁定
- 4.2.函數
- 4.3.原生類型
- 4.4.注釋
- 4.5.If語句
- 4.6.循環
- 4.7.所有權
- 4.8.引用和借用
- 4.9.生命周期
- 4.10.可變性
- 4.11.結構體
- 4.12.枚舉
- 4.13.匹配
- 4.14.模式
- 4.15.方法語法
- 4.16.Vectors
- 4.17.字符串
- 4.18.泛型
- 4.19.Traits
- 4.20.Drop
- 4.21.if let
- 4.22.trait 對象
- 4.23.閉包
- 4.24.通用函數調用語法
- 4.25.crate 和模塊
- 4.26.const和static
- 4.27.屬性
- 4.28.type別名
- 4.29.類型轉換
- 4.30.關聯類型
- 4.31.不定長類型
- 4.32.運算符和重載
- 4.33.Deref強制多態
- 4.34.宏
- 4.35.裸指針
- 4.36.不安全代碼
- 5.高效 Rust
- 5.1.棧和堆
- 5.2.測試
- 5.3.條件編譯
- 5.4.文檔
- 5.5.迭代器
- 5.6.并發
- 5.7.錯誤處理
- 5.8.選擇你的保證
- 5.9.外部函數接口
- 5.10.Borrow 和 AsRef
- 5.11.發布途徑
- 5.12.不使用標準庫
- 6.Rust 開發版
- 6.1.編譯器插件
- 6.2.內聯匯編
- 6.4.固有功能
- 6.5.語言項
- 6.6.鏈接進階
- 6.7.基準測試
- 6.8.裝箱語法和模式
- 6.9.切片模式
- 6.10.關聯常量
- 6.11.自定義內存分配器
- 7.詞匯表
- 8.語法索引
- 9.參考文獻
- 附錄:名詞中英文對照