Rust手动管理内存

为了确保安全性,Rust在零抽象的基础上,限制了很多易导致潜在bug的操作,比如直接指针操作,以及长生命周期等等,但是在某些情况下,如果涉及到操作比较底层的数据,往往难免自行维护内存。

本文主要以layout库来进行手动内存维护演示。

假设要实现一个内存分配器,但是需要满足以下条件:

  • 手动从堆中申请内存
  • 手动执行内存回收
  • 内存空间能够被复用

由于要手动回收内存,可行的方案是实现一个智能指针包裹裸指针,为了将裸指针指向的内存区视为待分配的数据结构,可以采用强制类型转换。

use std::alloc::{alloc, dealloc, Layout};
use std::ops::{Deref, DerefMut};
use std::fmt;
use fmt::Debug;
use std::collections::HashMap;

#[derive(Debug)]
struct Header {
    a: i32,
}

struct Cap<T>(*mut u8, Layout, std::marker::PhantomData<T>);

impl<T> Cap<T> {
    fn new() -> Self {
        let layout = Layout::new::<T>();
        unsafe {
            let ptr = alloc(layout);
            Cap(ptr, layout, std::marker::PhantomData)
        }
    }
}

impl<T: Debug> fmt::Display for Cap<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            write!(f, "({:?})", & *(self.0 as *mut T))
        }
    }
}

impl<T> Drop for Cap<T> {
    fn drop(&mut self) {}
}

impl<T> Deref for Cap<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe {
            & *(self.0 as *mut T)
        }
    }
}

impl<T> DerefMut for Cap<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe {
            &mut *(self.0 as *mut T)
        }
    }
}

struct MemCache<T> {
    cache_map: HashMap<usize, Vec<Cap<T>>>,
}

impl<T> MemCache<T> {
    fn new() ->Self {
        MemCache {
            cache_map: HashMap::new(),
        }
    }

    fn alloc(&mut self) -> Cap<T> {
        let obj_size = std::mem::size_of::<T>();
        match self.cache_map.get_mut(&obj_size) {
            Some(bucket) => {                
                if bucket.is_empty() {
                    return Cap::<T>::new()
                } else {
                    let ele = bucket.remove(0);
                    return ele
                }
            },
            _ => {
                return Cap::<T>::new();
            },
        }
    }

    fn drop(&mut self, d: Cap<T>) {
        let obj_size = std::mem::size_of::<T>(); 
        match self.cache_map.get_mut(&obj_size) {
            Some(bucket) => {
                bucket.push(d);
            }
            _ => {
                let mut b = Vec::new();
                b.push(d);
                self.cache_map.insert(obj_size, b);
            }
        };
    }
}

fn main() {
    let mut cache = MemCache::<Header>::new();

    let mut t1 = cache.alloc();
    t1.a = 38;
    println!("Cap {:#}", t1);
    cache.drop(t1);

    let mut t2 = cache.alloc();
    t2.a = 36;
    println!("Cap {:#}", t2);
    cache.drop(t2);
}

主要思路如下:

  • 用智能指针封装裸指针,在deref时,将裸指针转换为具体的类型
  • 释放智能指针时,用一个HashMap缓存住,在分配阶段如果有缓存则用缓存

上述的实现不能自动执行drop,需要手动执行,所以在CapDrop实现是一个空函数。

为了实现自动释放,需要在Cap中记录下MemCache,参考实现如下:

use std::fmt;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;

use crossbeam_queue::SegQueue;
use stable_deref_trait::StableDeref;

pub trait Allocate {
    fn alloc() -> Self;
}

#[derive(Debug)]
struct Header {
    a: i32,
}

impl Allocate for Header {
    fn alloc() -> Self {
        Header {
            a: 89,
        }
    }
}

pub struct BytePool<T>
where
    T: Allocate,
{
    small_bask: SegQueue<T>,
}

pub struct ByteBuffer<'a, T: Allocate> {
    data: mem::ManuallyDrop<T>,
    pool: &'a BytePool<T>,
}

impl<T: Allocate + fmt::Debug> fmt::Debug for ByteBuffer<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.data.deref())
    }
}

impl<T: Allocate + fmt::Debug> fmt::Display for ByteBuffer<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.data.deref())
    }
}

impl<T: Allocate> Default for BytePool<T> {
    fn default() -> Self {
        BytePool::<T> {
            small_bask: SegQueue::new(),
        }
    }
}

impl<T: Allocate> BytePool<T> {
    pub fn new() -> Self {
        BytePool::default()
    }

    pub fn alloc(&self) -> ByteBuffer<'_, T> {
        let list = &self.small_bask;
        if let Some(el) = list.pop() {
            return ByteBuffer::new(el, self);
        }

        let data = T::alloc();
        ByteBuffer::new(data, self)
    }

    fn push_raw_block(&self, buffer: T) {
        self.small_bask.push(buffer);
    }
}

impl<'a, T: Allocate> Drop for ByteBuffer<'a, T> {
    fn drop(&mut self) {
        let data = mem::ManuallyDrop::into_inner(unsafe { ptr::read(&self.data) });
        self.pool.push_raw_block(data);
    }
}

impl<'a, T: Allocate> ByteBuffer<'a, T> {
    fn new(data: T, pool: &'a BytePool<T>) -> Self {
        ByteBuffer {
            data: mem::ManuallyDrop::new(data),
            pool,
        }
    }

    pub fn size(&self) -> usize {
        std::mem::size_of::<T>()
    }
}

impl<'a, T: Allocate> Deref for ByteBuffer<'a, T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.data.deref()
    }
}

impl<'a, T: Allocate> DerefMut for ByteBuffer<'a, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.data.deref_mut()
    }
}

unsafe impl<'a, T: StableDeref + Allocate> StableDeref for ByteBuffer<'a, T> {}

fn main() {
    let p = BytePool::<Header>::new();
    {
        let mut t = p.alloc();
        t.a = 1;
        println!("{:#}", t);
    }

    let mut t = p.alloc();
    t.a = 1;
    println!("{:#}", t);
}
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • github地址:https://github.com/bradyjoestar/rustnotes(欢迎star...
    bradyjoestar阅读 2,952评论 0 6
  • rust中的指针大致可以分成三种:引用、裸指针和智能指针。 智能指针实际上是一种结构体,只不过它的行为类似指针。智...
    梁帆阅读 2,982评论 0 0
  • 变量 声明变量关键字:let 变量值分为两种类型: 可变的(mut) 不可变 变量类型: 布尔型 - bool 表...
    JennerHua阅读 893评论 0 4
  • 主要的内存管理方法 主要针对堆内存的管理。 手动管理(c/c++),容易触发内存安全问题,以及内存泄露。 自动管理...
    西门早柿阅读 969评论 0 0
  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,961评论 1 16