zoukankan      html  css  js  c++  java
  • STL内存管理

    其实对于一个标准的STL 容器,当vector vec 的真实语句应该是 vetor<int, allocator>vec,allocator是一个标准的配置器,其作用就是为各个容器管理内存。这里需要注意的是在SGI STL中,有两个配置器:allocator(标准的)和alloc(默认的配置器)

    注意:alloc(带poll)在3.3版本之前是默认的内存分配器,而allocator(3.4以后的版本)才是默认的内存分配器。所以,候杰的STL源码剖析过时了,它讲的只是3.3以前的版本

    allocator

    allocator不建议使用,它的效率不佳,只是对:: operator new和:: operator delete做了一层简单的封装而已

    #include <new>// for new
    #include <cstddef> //  size_t
    #include <climits> // for unit_max
    #include <iostream> // for cerr
    using namespace std;
    
    namespace SLD {
    template <class T>
    class allocator
    {
    public:
    	typedef T		value_type;
    	typedef T*		pointer;
    	typedef const T*	const_pointer;
    	typedef T&		reference;
    	typedef const T&	const_reference;
    	typedef size_t		size_type;
    	typedef ptrdiff_t	difference_type;
    
    	template <class U>
    	struct rebind
    	{
    		typedef allocator<U> other;
    	};
    
    	//申请内存
    	pointer allocate(size_type n, const void* hint = 0)
    	{
    		T* tmp = (T*)(::operator new((size_t)(n * sizeof(T))));
    		//operator new 和new operator是不同的
    		if (!tmp)
    			cerr << "out of memory"<<endl;
    		
    		return tmp;
    
    	}
    
    	//释放内存
    	void deallocate(pointer p)
    	{
    		::operator delete(p);
    	}
    	
    	//构造
    	void construct(pointer p, const T& value)
    	{
    		new(p) T1(value);
    	}
    	
    	//析构
    	void destroy(pointer p)
    	{
    		p->~T();
    	}
    	
    	//取地址
    	pointer address(reference x)
    	{
    		return (pointer)&x;
    	}
    	
    
    	const_pointer const_address(const_reference x)
    	{
    		return (const_pointer)&x;
    	}
    
    	size_type max_size() const 
    	{
    		return size_type(UINT_MAX/sizeof(T));
    	}
    };
    }
    

    SGI的std::alloc(全局共用一个内存池)

    在我们敲下如下代码:

    Foo *pf = new Foo;
    delete pf;
    

    其中的new有两阶段操作:

    1. 调用:: operator new配置内存
    2. 调用:: placement new构造对象
      delete也有两阶段操作:
    3. 调用析构函数析构对象
    4. 调用:: operator delete释放内存

    image

    • stl_construct.h:管理构造和析构
    • stl_allo.h:内存的申请和释放
    • stl_uninitialized.h:大片内存的申请和复制

    一级配置器和二级配置器的关系如下:
    image

    malloc_alloc_template

    在SGI STL中,如果申请的内存区域大于128B的时候,就会调用一级适配器,而一级适配器的调用也是非常简单的,直接用malloc申请内存,用free释放内存。但是malloc和free是C语言接口,为了模拟C++内存不足,另外提供了__set_malloc_handler,用来内存不足时回调

    当内存不足的时候,处理程序会不断地调用malloc_handler,但如果malloc_handler并未设定,就会直接抛出bad_alloc异常。

    default_alloc_template

    二级内存分配器,内部是一个内存池

    image

    二级配置器每次申请一大块内存,并由内部内存池和free_list管理。如果用户有同样的需求,直接从free_list取出,同样的,将用户归还的内存放入free_list中。维护了16根不同大小的free_list,分别是8,16,24...128。free_list的节点构造如下:

    union obj{
        union obj *free_list_link;
        char client_data[1];
    }
    

    通过这种方式,我们的free_list链表不会占用额外内存。(和tcmalloc的freelist一摸一样啊)

    二级配置器函数申请资源逻辑

    • 寻找合适的free_list
      • free_list非空,返回链表中第一块资源
      • free_list空,调用refill从内存池填充链表,返回第一块资源

    refill从调用chunk_alloc内存池获取资源处理逻辑

    1. refiil向内存池申请20个对象大小的资源
      1. 内存池有足够的资源配置20个对象,直接返回
      2. 内存池不足以配置20个对象,返回能够配置的最大个数
      3. 内存池一个对象都不能配置
        1. 首先将内存池剩余的资源给合适的free_list
        2. 调用malloc获取资源
          1. malloc失败,从free_list收集资源,仍然失败,调用一级配置器,希望oom handler会处理
          2. malloc成功,返回资源
    2. 将获取的资源形成链表

    这里有个例子:

    1. chunk_alloc(32,20),调用malloc申请40 * 32块资源。20个交给free_list,20个留给内存池
    2. chunk_alloc(64,20),返回10 * 64
    3. 万一malloc失败了,从free_list获取内存空间抢救一下。
    4. 如果free_list找不到,只能让一级配置器的new handler抢救了

    释放资源逻辑

    释放资源,首先从容器获取要释放对象的大小(这里和free不同,free并不需要知道),然后放入free_list,大家注意,居然不调用free。这样的话,随着这个alloc的析构(好像只有进程结束),里面申请的内存才回释放

    未初始化空间的构造和析构

    一共有5个函数干这事,除了construct,其他的函数都会判定构造或者析构函数是否是trival,然后进行优化。

    construct

    负责处理对象的构造,不会判定是否是POD

    destory

    负责对象的析构,会对是否trival进行优化
    image

    uninitialized_copy

    对象的批量拷贝

    uninitialized_fill

    负责对象批量填充

    uninitialized_fill_n

    负责对象的批量填充

    image

    小技巧(将内存上调到8的倍数)

    int round_up(int bytes){
        return ((bytes + ALIGN - 1) & ~(ALIGN - 1));
    }
    
  • 相关阅读:
    setInterval和setTimeOut方法—— 定时刷新
    json
    开发者必备的火狐插件
    C#泛型类和集合类的方法
    jQuery几种常用方法
    SQL语句优化技术分析
    索引的优点和缺点
    Repeater使用技巧
    jQuery 表格插件
    利用WebRequest来实现模拟浏览器通过Post方式向服务器提交数据
  • 原文地址:https://www.cnblogs.com/biterror/p/6913238.html
Copyright © 2011-2022 走看看