• C/C++内存管理器


    C标准库提供了malloc,free,calloc,realloc,C++标准库还提供了new, new[], delete, delete[]。这些用来管理内存,看起来够用了,为啥还要自己写一个内存管理器呢?

    原因还是从性能考虑:例如malloc和new是出于通用性考虑的,能处理多线程情况(multithread)。对于单线程的程序,这种额外的功能反而降低性能。
    而且还注意到,new/delete/free/malloc都是要在user-space和kernel-code做切换的,context的切换会降低性能。如果自己写一个user-land的内存管理器,就能大幅减少这种切换。还有就是GC(garbage collection)。

    几点要求

    1. 速度:比编译器的内存分配器要快才行
    2. 鲁棒:不能有内存泄漏,分配多少就收回多少
    3. 方便:用户不怎么需要改代码,就能把内存管理器加进去
    4. 移植:应当跨平台,用户在啥系统上都能用,方便移植

    前人经验

    1. 申请大块内存
      一次性申请一大块内存,减少向系统申请的次数,以后需要申请内存就从这一大块上分配。
      (这不就是缓存么。。)
    2. 为特定尺寸优化
      任何程序中都一种最常见的内存申请尺寸。为这种尺寸优化,提升性能。
    3. 需要删除的内存暂时存放在容器中(敝帚自珍)
      从用户角度看,变量声明周期结束,要释放分配的内存;但是内存管理器实际上可以“不真的把这块内存还给系统”,而是攒起来留给后续需要分配内存时用。当然,这种内存更多的是碎片,所以再分配时可能不够用,那就得再找大块内存去分配了。

    代码,版本1

    首先是一个不使用内存管理器的代码,内存的申请和释放是手动完成的,并且放在for循环中,来频繁的申请和释放,方法这种做法的效果(慢啊)。代码:

    #include <ctime>
    #include <iostream>
    using namespace std;
     
    class Complex
    {
    public:
    	Complex(double a, double b) : r(a),c(b)
    	{
    	}
    private:
    	double r;				//实部
    	double c;				//虚部
    };
     
    int main(int argc, char* argv[])
    {
    	Complex* array[1000];
    	clock_t t1,t2;
    	t1 = clock();
    	for (int i = 0; i < 5000; i++)
    	{
    		for (int j = 0; j < 1000; j++)
    		{
    			array[j] = new Complex(i,j);
    		}
    		for (int j=0;j<1000;j++)
    		{
    			delete array[j];
    		}
    	}
    	t2 = clock();
    	cout << double(t2-t1)/CLOCKS_PER_SEC << "s" << endl;
    	return 0;
    }
    

    代码,版本2

    这次用一个内存管理类来托管内存的申请和释放,并且原有的Complex类上仅仅是重载了new/delete/new[]/delete[]这四个operator。放码过来:

    #include <iostream>
    #include <sys/types.h>
    
    using namespace std;
    
    
    class IMemoryManager{
    public:
        virtual void* allocate(size_t) = 0;
        virtual void  free(void*) = 0;
    };
    
    
    class MemoryManager: public IMemoryManager{
    public:
        MemoryManager(){
            freestorehead = NULL;
            ExpandPoolSize();
        }
        ~MemoryManager(){
            CleanUp();
        }
        void* allocate(size_t);
        void  free(void*);
    private:
        struct FreeStore{
            FreeStore* next;
        };
        void ExpandPoolSize();
        void CleanUp();
        FreeStore* freestorehead;
    };
    
    MemoryManager gMemoryManager;
    
    class Complex {
    public:
        Complex(double a, double b): r(a), c(b){}
    
        inline void* operator new(size_t size){
            return gMemoryManager.allocate(size);
        }
    
        inline void operator delete(void* object){
            gMemoryManager.free(object);
        }
    
        inline void* operator new[](size_t size){
            return gMemoryManager.allocate(size);
        }
    
        inline void operator delete[](void* object){
            return gMemoryManager.free(object);
        }
    private:
        double r;
        double c;
    };
    
    
    void* MemoryManager::allocate(size_t size){
        if (0==freestorehead){
            ExpandPoolSize();
        }
        FreeStore* head = freestorehead;
        freestorehead = head->next;
        return head;
    }
    
    void MemoryManager::free(void* object){
        FreeStore* head = static_cast<FreeStore*>(object);
        head->next = freestorehead;
        freestorehead = head;
    }
    
    const int POOLSIZE = 128;
    
    void MemoryManager::ExpandPoolSize(){
        size_t size = max(sizeof(Complex), sizeof(FreeStore*));
        FreeStore* head = reinterpret_cast<FreeStore*>(new char[size]);
        freestorehead = head;
    
        for(int i=0; i<POOLSIZE; i++){
            head->next = reinterpret_cast<FreeStore*>(new char[size]);
            head = head->next;
        }
        head->next = 0;
    }
    
    void MemoryManager::CleanUp(){
        FreeStore* nextPtr = freestorehead;
        for(; nextPtr; nextPtr=freestorehead){
            nextPtr = freestorehead;
            freestorehead = freestorehead->next;
            delete[] nextPtr;
        }
    }
    
    
    int main(int argc, char* argv[])
    {
        Complex* array[1000];
        clock_t t1,t2;
        t1 = clock();
        for (int i = 0; i < 5000; i++)
        {   
            for (int j = 0; j < 1000; j++)
            {   
                array[j] = new Complex(i,j);
            }   
            for (int j=0;j<1000;j++)
            {   
                delete array[j];
            }   
        }   
        t2 = clock();
        cout << double(t2-t1)/CLOCKS_PER_SEC << "s" << endl;
        return 0;
    }
    

    分析

    时间开销对比

    g++ main_v1.cpp -o main_v1 -O3
    ./main_v1
    0.217214s
    
    g++ main_v2.cpp -o main_v2 -O3
    ./main_v2
    0.026611s
    

    两者的时间开销竟然相差一个数量级。

    代码的正确性
    其实new()申请内存的代码很不严谨,没有检查形参size是否会超过预设POOLSIZE大小,只不过通常情况下单次的size肯定小于POOLSIZE,但是极端情况下,或者一口气申请了多个变量的内存,可能会越界。另外,只适用于单线程。

    以上内容来自IBM的一篇教程,还有很多内容没能看完和理解,挖坑带填:https://www.ibm.com/developerworks/aix/tutorials/au-memorymanager/index.html

  • 相关阅读:
    服务器消息机制实现记录
    转载SQL经典代码按某一字段分组取最大(小)值所在行的数据
    记录js获取当前URL
    (原创)xilinx IP建立向导创建的目录和文件都是做什么的?由错误ERROR:HDLCompiler:Instantiating <xx> from unknown module <xx>引发的思考
    [转]NTFS3G的安装和配置
    (原创)Notepad++怎么实现双视图/双窗口?
    (原创)Quartus硬件工程路径改变,nios工程该怎么办?
    (原)verilog中的reg类型变量,一定会综合出触发器吗?
    (Windows)使用纯净版本的系统碟安装系统后没有网卡驱动怎么办?
    [转]NIOS_II的Boot过程分析
  • 原文地址:https://www.cnblogs.com/zjutzz/p/9515252.html
Copyright © 2020-2023  润新知