SGI的内存管理

1. SGI中的内存管理

在SGI中, 其内存分配把这两步独立出了两个函数:allocate 申请内存,construct 调用构造函数,分别在

2.构造和析构的工具

在stl_construct.h中

//第一个版本destory接受一个指针,然后调用析构函数
template <class T>
inline void destroy(T* pointer) {
    pointer->~T();
}

//构造函数
template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
  new (p) T1(value);
}

//第二个版本的destory,接收两个迭代器,并根据元素的值的类型来调用析构函数,为__false_type,调用析构,为__true_type,直接不管。
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
  for ( ; first < last; ++first)
    destroy(&*first);
}
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
  typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
  __destroy_aux(first, last, trivial_destructor());
}
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
  __destroy(first, last, value_type(first));
}
//特化版本的destroy函数。
inline void destroy(char*, char*) {}
inline void destroy(wchar_t*, wchar_t*) {}

3.空间配置与释放

考虑到小型区块可能造成的内存破碎问题,SGI设计了双层级配置器。第一级配置器直接使用malloc()和free(),第二级配置器对具体问题使用不同的策略。

3.1 第一级配置器
class __malloc_alloc_template {
private:
//以下函数处理内存不足的情况
//oom - out of memory
static void *oom_malloc(size_t);
static void *oom_realloc(void *, size_t);

public:
static void * allocate(size_t n)
{
    void *result = malloc(n);  //第一级配置器直接调用malloc
    if (0 == result) result = oom_malloc(n);//无法满足需求调用oom_malloc(n)
    return result;
}
static void deallocate(void *p, size_t /* n */)
{
    free(p);
}
static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
{
    void * result = realloc(p, new_sz);
    if (0 == result) result = oom_realloc(p, new_sz);
    return result;
}
//参数为void f(),返回值也为void f(),用于自己指定oom handle
static void (* set_malloc_handler(void (*f)()))()
{
    void (* old)() = __malloc_alloc_oom_handler;
    __malloc_alloc_oom_handler = f;
    return(old);
}

};

void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;//初始值为0

template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
{
    void (* my_malloc_handler)();//声明一个函数指针
    void *result;
    for (;;) {//一直尝试释放,申请
        my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
        (*my_malloc_handler)();
        result = malloc(n);
        if (result) return(result);
    }
}
template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)
{
    void (* my_malloc_handler)();
    void *result;
    for (;;) {
        my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
        (*my_malloc_handler)();
        result = realloc(p, n);
        if (result) return(result);
    }
}
3.2 二级配置器
template <bool threads, int inst>
class __default_alloc_template {
private:
  // Really we should use static const int x = N
  // instead of enum { x = N }, but few compilers accept the former.
    enum {__ALIGN = 8};     //小型区块的上调边界
    enum {__MAX_BYTES = 128};//小型区块的上限
    enum {__NFREELISTS = __MAX_BYTES/__ALIGN};//free_list个数
  static size_t ROUND_UP(size_t bytes) {
        return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));//上调的__ALIGN的倍数
  }
__PRIVATE:
  //obj是节点。
  union obj {
        union obj * free_list_link;
        char client_data[1];    /* The client sees this.        */
  };
private:
    static obj * __VOLATILE free_list[];
    //获得节点的序号,0开始
  static  size_t FREELIST_INDEX(size_t bytes) {
        return (((bytes) + __ALIGN-1)/__ALIGN - 1);
  }
  // Returns an object of size n, and optionally adds to size n free list.
  static void *refill(size_t n);
  // Allocates a chunk for nobjs of size "size".  nobjs may be reduced
  // if it is inconvenient to allocate the requested number.
  static char *chunk_alloc(size_t size, int &nobjs);
  // Chunk allocation state.
  static char *start_free;      //内存池的起始位置
  static char *end_free;        //内存池的结束位置
  static size_t heap_size;      //堆的大小
public:
  /* n must be > 0      */
    //yk:内存分配
  static void * allocate(size_t n)
  {  //后面介绍
  };
  /* p may not be 0 */
  //yk : free memory
  static void deallocate(void *p, size_t n)
  {//后面介绍
  }
  // reallocate memory
  static void * reallocate(void *p, size_t old_sz, size_t new_sz);
} ;
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
typedef __default_alloc_template<false, 0> single_client_alloc;
//成员函数的初始化
template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::start_free = 0;
template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::end_free = 0;
template <bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;
template <bool threads, int inst>
__default_alloc_template<threads, inst>::obj * __VOLATILE
__default_alloc_template<threads, inst> ::free_list[
# ifdef __SUNPRO_CC
    __NFREELISTS
# else
    __default_alloc_template<threads, inst>::__NFREELISTS
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
// The 16 zeros are necessary to make version 4.1 of the SunPro
// compiler happy.  Otherwise it appears to allocate too little
// space for the array.

我们最关心的有三点:1. 内存池的创建。2.内存的分配。 3. 内存的释放。

1、SGI内存池的结构

内部的结构体为:

union obj {
        union obj * free_list_link;
        char client_data[1];    /* The client sees this.        */
  };

一个free_list指针指向一个128byte的空间,每个空间根据管理的大小(8,16,32……128),有不同个数的节点数目。为了节省指针的空间, 每个节点为一个union.其中的free_list_link指向下一个空的节点,client_data为柔性数组,大小为当前空间中每个区块的大小。

static obj * __VOLATILE free_list[];

为一个free_list数组,有16个free_list,每个free_list也为一个obj,它有一个指针,指向自己管理的空间第一个节点,它的free_list_link指向第一个自己管理的大小的空间。比如说,#11号区域,刚开始的时候,它是管理一个96byte的空间的,它自己的占用的空间存放的是这个96byte的起始地址,它的free_list_link也是指向这个地址,如果这个空间被别人申请了,那么,申请的时候就会让它指向下一个96byte的空间。

2、内存分配

内存分配的源码如下:

  static void * allocate(size_t n)
  {
    obj * __VOLATILE * my_free_list;
    obj * __RESTRICT result;
    if (n > (size_t) __MAX_BYTES) {//大于128byte,直接调用一级配置器
        return(malloc_alloc::allocate(n));
    }
    my_free_list = free_list + FREELIST_INDEX(n);//找到合适的序号的地址
    result = *my_free_list;
    if (result == 0) {//没有找到合适的,准备重新填充
        void *r = refill(ROUND_UP(n));
        return r;
    }
    *my_free_list = result -> free_list_link;//重新调整free_list
    return (result);
  };

3、空间释放

 static void deallocate(void *p, size_t n)
  {
    obj *q = (obj *)p;
    obj * __VOLATILE * my_free_list;
    if (n > (size_t) __MAX_BYTES) {//大于128byte,调用第一级配置器
        malloc_alloc::deallocate(p, n);
        return;
    }
    //否则回收
    my_free_list = free_list + FREELIST_INDEX(n);

    q -> free_list_link = *my_free_list;
    *my_free_list = q;
  }

4、重新填充 free_list,

当free_list中没有可用区块了,就refill(),准备为free_list重新填充空间。新的空间将取自内存池,一般取20个新节点,如果空间不足的话,可能取不到。

template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{
    int nobjs = 20;//默认20个区块
    char * chunk = chunk_alloc(n, nobjs);//调用chunk_alloc
    obj * __VOLATILE * my_free_list;
    obj * result;
    obj * current_obj, * next_obj;
    int i;
    if (1 == nobjs) return(chunk);//只申请到一个区块,也是惨,直接返回这个区块
    my_free_list = free_list + FREELIST_INDEX(n);//申请的比较多的话,my_free_list现在指向n大小的free_list
    /* Build free list in chunk */
    //以下在申请的空间创建free_list
      result = (obj *)chunk;//保存返回值
      *my_free_list = next_obj = (obj *)(chunk + n);//这是第一个
      for (i = 1; ; i++) {//从第一个开始,第0个给客户端了。
        current_obj = next_obj;
        next_obj = (obj *)((char *)next_obj + n);
        if (nobjs - 1 == i) {//没有了
            current_obj -> free_list_link = 0;
            break;
        } else {//如果还有,指向下一个obj
            current_obj -> free_list_link = next_obj;
        }
      }
    return(result);
}

5、内存池

template <bool threads, int inst>
char*
__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{//开始申请空间
    char * result;
    size_t total_bytes = size * nobjs;//想要申请的空间,想想就好
    size_t bytes_left = end_free - start_free;//剩余的空间
    if (bytes_left >= total_bytes) {//剩余的空间比较多,直接给他就好了
        result = start_free;
        start_free += total_bytes;
        return(result);
    } else if (bytes_left >= size) {//如果剩余空间不多,但还是至少有一个size的大小
        nobjs = bytes_left/size;    //就尽量分给他
        total_bytes = size * nobjs;
        result = start_free;
        start_free += total_bytes;
        return(result);
    } else {//实在没有办法,只能去malloc了
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        // Try to make use of the left-over piece.
        if (bytes_left > 0) {//剩余的空间,直接放到free_list中。因为申请的时候是按8的倍数来申请
            //这里不会有小区块的问题
            obj * __VOLATILE * my_free_list =
                        free_list + FREELIST_INDEX(bytes_left);
            ((obj *)start_free) -> free_list_link = *my_free_list;
            *my_free_list = (obj *)start_free;
        }
        //开始申请了
        start_free = (char *)malloc(bytes_to_get);
        if (0 == start_free) {//oh,没有申请成功
            int i;
            obj * __VOLATILE * my_free_list, *p;
            // Try to make do with what we have.  That can‘t
            // hurt.  We do not try smaller requests, since that tends
            // to result in disaster on multi-process machines.
            //从自己有的资源里面去找
            for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
                my_free_list = free_list + FREELIST_INDEX(i);
                p = *my_free_list;
                if (0 != p) {//这是找到了
                    *my_free_list = p -> free_list_link;
                    start_free = (char *)p;
                    end_free = start_free + i;
                    return(chunk_alloc(size, nobjs));//调整一下
                    // Any leftover piece will eventually make it to the
                    // right free list.
                }
            }
        end_free = 0;   // In case of exception.实在没有空间了,调用一级配置器,看看oom机制
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
            // This should either throw an
            // exception or remedy the situation.  Thus we assume it
            // succeeded.
        }
        //这是申请成功的,调用自己,修正nobjs
        heap_size += bytes_to_get;
        end_free = start_free + bytes_to_get;
        return(chunk_alloc(size, nobjs));
    }
}

举个例子:

1.调用chunk_allock(32,20):malloc分配40个32byte区域,第一块个客户端,19块给free_list[3],剩余20块给内存池

2.调用chunk_allock(64,20):free_list[7]为空,那么向内存池请求,内存池只有10个,就把这10个给他,第一个交给客户端,剩余9个给free_list[7]维护

3(自己加的).调用chunk_allock(32, 20):free_list[6]为空,内存池啥也没有,如果malloc失败,在自己free_list[7]找到一块,把free_list[7]的一个拿出来释放到内存池,然后调用自己,这里,内存池现在有64byte,因此可以申请两个32bytes,一个给客户端,一个由free_list[6]维护

4.接下来调用chunk_alloc(96,20):free_list[11]为空,向内存池请求,内存池也没有,只能调用malloc(),40+n个96bytes区块,d第一个给客户端,19个给free_list[11],剩余20+n给内存池

时间: 2024-10-05 11:38:26

SGI的内存管理的相关文章

SGI STL内存管理

在SGI STL版本的内存管理中,使用这样一种方式来分配内存:内存分配+对象初始化.首先是分配内存,其次是根据对象的类型(是否为POD[Plain of Data])来使用最有效的方式来初始化对象.回收内存也是用同样的方式:析构对象+回收内存,根据对象是否为POD类型,确定最有效的析构方式. SGI STL使用双层级配置器,第一级配置器直接使用malloc()和free(),第二级根据如下策略:当配置区块>128 Bytes时,视之为"足够大",调用一级配置器,否则视之为过小,调

SGI STL内存配置器存在内存泄漏吗?

阅读了SGI的源码后对STL很是膜拜,很高质量的源码,从中学到了很多.温故而知新!下文中所有STL如无特殊说明均指SGI版本实现. STL 内存配置器 STL对内存管理最核心部分我觉得是其将C++对象创建过程分解为构造.析构和内存分配.释放两类操作分离开来!摆脱了对频繁调用new或malloc函数想操作系统申请空间而造成的低效.其中析构操作时对具有non-trival.trival 析构函数的class区别对待也提高了效率.SGI 的两级配置器结构属于锦上添花. STL内存配置器有没有内存泄漏?

STL内存管理

过年在家无事看了<STL源码剖析>,开学了将看过的东西总结一下,以防忘记. 先从STL的内存管理开始总结.掌管STL内存控制的是一个叫空间适配器(alloc)的东西.STL有两个空间适配器,SGI标准空间适配器(allocate)和SGI特殊的空间适配器(alloc),前者只是对c++的new和delete进行了简单的封装.下面主要介绍的是alloc空间适配器. 一般而言,c++的内存配置和释放操作是这样的: class Foo {...} Foo* pf = new Foo: delete

SGI STL内存配置器(一):内存泄漏?

阅读了Alexander大神的SGI STL源码,膜拜,很高质量的源码,获益匪浅.温故而知新!下文中所有STL如无特殊说明均指SGI版本实现. STL 内存配置器 STL对内存管理最核心部分我觉得是它将C++对象创建过程分解为构造.析构和内存分配.释放!摆脱了由于频繁调用new或malloc函数向操作系统申请空间而造成的低效.其中析构操作时对具有non-trival.trival 析构函数的class区别对待也提高了效率.至于SGI的两级配置器结构则属于锦上添花的类型. STL两级结构的内存配置

linux内存管理

一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分:    1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程序可调用它.假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段.     2) 数据段:存放已初始化的全局变量.静态变量(包括全局和局部的).常量.static全局变量和static函数只能在当前文件中被调用.     3) 未初始化数据区(uninitializeddata s

Block内存管理实例分析

在ios开发中,相信说道block大家都不陌生,内存管理问题也是开发者最头疼的问题,网上很多讲block的博客,但大都是理论性多点,今天结合一些实例来讲解下. 存储域 首先和大家聊聊block的存储域,根据block在内存中的位置,block被分为三种类型: NSGlobalBlock NSStackBlock NSMallocBlock 从字面意思上大家也可以看出来 NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中. NSStackBlock是位于

Obstack是C标准库里面对内存管理的GNU扩展

Obstack介绍 Obstack初始化 在Obstack中申请对象 释放对象 申请growing object 获取Obstack状态 数据对齐 以下是来自wiki对obstack的介绍: Obstack是C标准库里面对内存管理的GNU扩展(实际上就是GNU C library了).Obstack===Object stack.没错,Obstack就是一个栈,栈里面的元素是对象object(不是面向对象的对象哦,这里的对象单指数据元素).这些数据是动态的,也就是使用的是动态内存.这种内存管理技

OC内存管理

OC内存管理 一.基本原理 (一)为什么要进行内存管理. 由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等. 管理范围:任何继承NSObject的对象,对其他的基本数据类型无效. 本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也

39-oc集合中对象的内存管理

集合中对象的内存管理 集合的内存管理原则是什么 当把一个对象添加到集合中时,这个对象会做了一次retain操作,计数器会+1 当一个集合被销毁时,会对集合里面的所有对象做一次release操作,计数器会-1 当一个对象从集合中移除时,会对这个对象做一次release操作,计数器会-1 集合方法的普遍规律是什么 如果方法名是add\insert开头,那么被添加的对象,计数器会+1 如果方法名是remove\delete开头,那么被移除的对象,计数器-1