STL源码剖析(二)第一级空间配置器

STL源码剖析(二)第一级空间配置器

一、空间配置器的作用

在学习空间配置器之前,我们需要先知道它是用来干嘛的,简单的说,它就是给容器分配内存的工具,每个容器都需要指定一个空间配置器,用于分配内存和释放内存

我们先看一眼vector容器的源码

template <class T, class Alloc = alloc>
class vector {
	typedef simple_alloc<value_type, Alloc> data_allocator;
	...
};

其中的Alloc就是空间配置器,缺省情况下指定的空间分配器为allocalloc是STL已经实现好的一个空间配置器

simple_alloc对其进行简单的封装,让这个空间设配器变得更加好用

vector容器中在需要内存的时候,会通过以下方法获取内存

data_allocator::allocate(len);

由上述可以知道,data_allocatorsimple_alloc,上面说simple_alloc是对空间配置器的简单封装,我们再来看一看simple_alloc的定义

template<class T, class Alloc>
class simple_alloc {

public:
    static T *allocate(size_t n)
                { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }
	...
};

simple_alloc其实就是调用我们指定的空间配置器的静态分配函数allocate来分配内存

当vector容器需要释放内存的时候,会通过以下方法

data_allocator::deallocate(p, n);

我们可以知道其定义在simple_alloc

template<class T, class Alloc>
class simple_alloc {
public:
    static void deallocate(T *p, size_t n)
                { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }
};

从上述我们可以看到,使用simple_alloc释放内存的时候,就是调用我们指定的空间配置器的静态释放函数deallocate来释放内存

到这里我们就清楚空间配置器的作用了

空间配置器就是提供两个静态方法来分配和释放内存,通过allocate分配内存,通过deallocate释放内存

至于空间配置器内部是如何实现的,那么就需要查看源码了

二、空间配置器定义

在查看空间配置器的源码前,我们需要先找到其定义,在我提供的这份STL源码中,在stl_alloc.h文件中,有如下的代码定义

# ifdef __USE_MALLOC
typedef malloc_alloc alloc; 
...
# else
typedef default_alloc_template<NODE_ALLOCATOR_THREADS, 0> alloc; 
...
#endif

可以看到alloc要么是malloc_alloc要么是default_alloc_template,至于是哪一种,由宏定义__USE_MALLOC决定

其中malloc_alloc较为简单,几乎没有内存管理,我们称它为第一级空间配置器

default_alloc_template较为复杂,但对内存管理非常精细,我们称它为第二级空间配置器

本文我们只讨论第一级空间配置器,第二级空间配置器将在下一篇文章中讨论

三、第一级空间配置器源码分析

接下来查看malloc_alloc的定义(这里我忽略了很多,只保留最重要的部分)

typedef __malloc_alloc_template<0> malloc_alloc;
class __malloc_alloc_template {
    ...
    static void (* __malloc_alloc_oom_handler)(); //malloc失败时会调用此函数处理
public:
    /* 分配内存 */
    static void * allocate(size_t n)
    {
        void *result = malloc(n);
        if (0 == result) result = oom_malloc(n);
        return result;
    }

    /* 释放内存 */
    static void deallocate(void *p, size_t /* n */)
    {
        free(p);
    }
     
    /* 设置内存分配失败时的处理函数 */
	static void (* set_malloc_handler(void (*f)()))()
    {
        void (* old)() = __malloc_alloc_oom_handler;
        __malloc_alloc_oom_handler = f;
        return(old);
    }
};

我们可以看到allocate通过malloc分配内存,deallocate通过free释放内存

至于deallocate过于简单,没有什么可谈的,下面来看一看allocate

class __malloc_alloc_template {
    ...
public:
    static void * allocate(size_t n)
    {
        void *result = malloc(n);
        if (0 == result) result = oom_malloc(n);
        return result;
    }
    ...
};

可以看到allocate通过malloc分配内存,如果分配失败就会调用oom_malloc,我们重点查看oom_malloc是如何处理内存分配失败的

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); //分配成功就返回
    }
}

可以看到oom_malloc就是循环的地调用__malloc_alloc_oom_handler函数处理,渴望__malloc_alloc_oom_handler能够释放掉一些内存,使得malloc能够分配到内存,如果malloc分配到内存,那么就返回

那么__malloc_alloc_oom_handler又是那个函数呢?

你回过头看一下__malloc_alloc_template的定义,__malloc_alloc_oom_handler是一个静态的函数指针,用户可以通过set_malloc_handler函数来设置它

其实上面这个就是仿造了C++ set_newhandler机制的一个过程

好了,对于第一级空间配置器就分析到这里了,确实非常简单,下一篇文章再来分析较为复杂的第二级空间配置器

发布了107 篇原创文章 · 获赞 197 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/weixin_42462202/article/details/101109277