精华内容
下载资源
问答
  • boost::pool内存池使用
    2020-03-26 11:44:25

    1、boost::pool只能作为普通数据类型的内存池,如int、double等,因为他只分配内存,不调用构造函数。

    2、如果内存分配失败,不抛异常,我们应该判断返回的指针是不是nullptr,防止空指针错误。

    3、我们一般不应该手工调用free()等释放内存的函数,应该交给内存池自动进行管理。如果你确定内存池 的空间不够,这是你可以调用malloc()对应的free()函数,释放一些已经分配的空间。

    4、示例:

    #include <iostream>
    #include <boost/pool/pool.hpp>
    
    int main()
    {
    	//p1.malloc()每次分配一块内存,大小是构造函数大小:1400 * 1024
    	{
    		unsigned char *p[800] = { nullptr };
    		boost::pool<> p1(1400 * 1024);
    		for (int i = 0; i < 800; ++i)
    		{
    			p[i] = static_cast<unsigned char*>(p1.malloc());
    			if (p[i] == nullptr)
    				std::cout << "第 " << i << " 块内存分配失败!" << std::endl;
    		}
    
    		//测试一下分配的内存是否可用
    		unsigned char* pp = new unsigned char[1400 * 1024];
    		memcpy_s(p[0], 1400 * 1024, pp, 1400 * 1024);
    		memcpy_s(p[799], 1400 * 1024, pp, 1400 * 1024);
    		if (pp) delete[]pp;
    	}
    	
    	//
    更多相关内容
  • C语言内存池使用模型

    2021-01-21 17:13:47
    C语言开发时,特别是在服务器端,内存的使用会成为系统性能的一个瓶颈,如频繁的分配和释放内存,会不断的增加系统的内存碎片,影响内核之后分配内存的效率,这个时候一个比较可行的做法是采用内存池,先分配好...
  • 使用内存池能极大的提高内存分配效率,并且能相应减少系统内存碎片。 -------- 单元内存池 单元内存池是以一个固定的单元长度为分配内存长度的内存池,只能分配固定长度的内存;单元内存池具有效率极高,消耗少,...
  • linux 内存池三方库 了他你就不需要自己编写内存池了.zip
  • 使用C语言编写的一个可以用来使用内存池,编写方式达到内存0碎片的标准。
  • Boost内存池使用与测试

    千次阅读 2018-08-02 21:58:23
    例如,当程序使用内存池内存池恰好处于已经满了的状态,那么这次内存申请会导致内存池自我扩充,肯定比直接new一块内存要慢。但在大部分时候,内存池要比new或者malloc快很多。 内存池效率测试 测试1:...

    转自 http://tech.it168.com/a2011/0726/1223/000001223399_all.shtml

    • Boost库是一个可移植的开源C++函数库,鉴于STL(标准模板库)已经成为C++语言的一个组成部分,可以毫不夸张的说,Boost是目前影响最大的通用C++库。Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容,是一个“准”标准库。
    • Boost内存池,即boost.pool库,是由Boost提供的一个用于内存池管理的开源C++库。作为Boost中影响较大的一个库,Pool已经被广泛使用。

    Boost内存池使用与测试

    什么是内存池

       是在计算机技术中经常使用的一种设计模式,其内涵在于:将程序中需要经常使用的核心资源先申请出来,放到一个池内,由程序自己管理,这样可以提高资源的使用效率,也可以保证本程序占有的资源数量
    经常使用的池技术包括内存池、线程池和连接池等,其中尤以内存池和线程池使用最多。
    内存池(Memory Pool) 是一种动态内存分配与管理技术。
    通常情况下,程序员习惯直接使用 new、delete、malloc、free 等API申请分配和释放内存,这样导致的后果是:当程序长时间运行时,由于所申请内存块的大小不定,频繁使用时会造成大量的内存碎片从而降低程序和操作系统的性能
    内存池则是在真正使用内存之前,先申请分配一大块内存(内存池)留作备用,当程序员申请内存时,从池中取出一块动态分配,当程序员释放内存时,将释放的内存再放入池内,并尽量与周边的空闲内存块合并。若内存池不够时,则自动扩大内存池,从操作系统中申请更大的内存池。

    内存池的应用场景

      早期的内存池技术是为了专门解决那种频繁申请和释放相同大小内存块的程序,因此早期的一些内存池都是用相同大小的内存块链表组织起来的。
      Boost的内存池则对内存块的大小是否相同没有限制,因此只要是频繁动态申请释放内存的长时间运行程序,都适用Boost内存池。这样可以有效减少内存碎片并提高程序运行效率。

    安装

      Boost的pool库是以C++头文件的形式提供的,不需要安装,也没有lib或者dll文件,仅仅需要将头文件包含到你的C++工程中就可以了。Boost的最新版本可以到 http://www.boost.org/ 下载。

    内存池的特征

    无内存泄露

    正确的使用内存池的申请和释放函数不会造成内存泄露,更重要的是,即使不正确的使用了申请和释放函数,内存池中的内存也会在进程结束时被全部自动释放,不会造成系统的内存泄露。

    申请的内存数组没有被填充

      例如一个元素的内存大小为A,那么元素数组若包含n个元素,则该数组的内存大小必然是A*n,不会有多余的内存来填充该数组。尽管每个元素也许包含一些填充的东西。

    任何数组内存块的位置都和使用operator new[]分配的内存块位置一致

      这表明你仍可以使用那些通过数组指针计算内存块位置的算法。

    内存池要比直接使用系统的动态内存分配快

      这个快是概率意义上的,不是每个时刻,每种内存池都比直接使用new或者malloc快。例如,当程序使用内存池时内存池恰好处于已经满了的状态,那么这次内存申请会导致内存池自我扩充,肯定比直接new一块内存要慢。但在大部分时候,内存池要比new或者malloc快很多。

    内存池效率测试

    测试1:连续申请和连续释放

      分别用内存池和new连续申请和连续释放大量的内存块,比较其运行速度,代码如下:

    #include "stdafx.h"
    #include <iostream>
    #include <ctime>
    #include <vector>
    #include <boost/pool/pool.hpp>
    #include <boost/pool/object_pool.hpp>
    using namespace std;
    using namespace boost;
    
    const int MAXLENGTH = 100000;
    
    int main ( )
    {
        boost::pool<> p(sizeof(int));
        int* vec1[MAXLENGTH];
        int* vec2[MAXLENGTH];
    
        clock_t clock_begin = clock();
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            vec1[i] = static_cast<int*>(p.malloc());
        }
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            p.free(vec1[i]);
            vec1[i] = NULL;
        }
    
        clock_t clock_end = clock();
        cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        clock_begin = clock();
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            vec2[i] = new int;
        }
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            delete vec2[i];
            vec2[i] = NULL;
        }
    
        clock_end = clock();
        cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        return 0;
    }

      测试环境:VS2008,WindowXP SP2,Pentium 4 CPU双核,1.5GB内存。
    image.png

      结论:在连续申请和连续释放10万块内存的情况下,使用内存池耗时是使用new耗时的47.46%。

    测试2:反复申请和释放小块内存

    代码如下:

    #include "stdafx.h"
    #include <iostream>
    #include <ctime>
    #include <vector>
    #include <boost/pool/pool.hpp>
    #include <boost/pool/object_pool.hpp>
    using namespace std;
    using namespace boost;
    
    const int MAXLENGTH = 500000;
    
    int main ( )
    {
        boost::pool<> p(sizeof(int));
    
        clock_t clock_begin = clock();
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            int * t = static_cast<int*>(p.malloc());
            p.free(t);
        }
        clock_t clock_end = clock();
        cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        clock_begin = clock();
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            int* t = new int;
            delete t;
        }
        clock_end = clock();
        cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        return 0;
    }
    

      测试结果如下:
    image.png

      结论:在反复申请和释放50万次内存的情况下,使用内存池耗时是使用new耗时的64.34%。

    测试3:反复申请和释放C++对象

      C++对象在动态申请和释放时,不仅要进行内存操作,同时还要调用构造和析购函数。因此有必要对C++对象也进行内存池的测试。
      代码如下:

    #include "stdafx.h"
    #include <iostream>
    #include <ctime>
    #include <vector>
    #include <boost/pool/pool.hpp>
    #include <boost/pool/object_pool.hpp>
    using namespace std;
    using namespace boost;
    
    const int MAXLENGTH = 500000;
    class A
    {
    public: 
        A()
        {
            m_i++; 
        }
        ~A( )
        {
            m_i--; 
        }
    private:
        int m_i;
    };
    
    int main ( )
    {
        object_pool<A> q;
    
        clock_t clock_begin = clock();
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            A* a = q.construct();
            q.destroy(a);
        }
    
        clock_t clock_end = clock();
        cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        clock_begin = clock();
        for (int i = 0; i < MAXLENGTH; ++i)
        {
            A* a = new A; 
            delete a;
        }
        clock_end = clock();
        cout<<"程序运行了 "<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        return 0;
    }

      测试结果如下:
    image.png

      结论:在反复申请和释放50万个C++对象的情况下,使用内存池耗时是使用new耗时的112.03%。这是因为内存池的construct和destroy函数增加了函数调用次数的原因。这种情况下使用内存池并不能获得性能上的优化。

    Boost内存池的分类

      Boost内存池按照不同的理念分为四类。主要是两种理念的不同造成了这样的分类。
      一是Object Usage和Singleton Usage的不同。
    - Object Usage意味着每个内存池都是一个可以创建和销毁的对象,一旦内存池被销毁则其所分配的所有内存都会被释放。
    - Singleton Usage意味着每个内存池都是一个被静态分配的对象,直至程序结束才会被销毁,这也意味着这样的内存池是多线程安全的。只有使用release_memory或者 purge_memory方法才能释放内存。
      二是内存溢出的处理方式。第一种方式是返回NULL代表内存池溢出了;第二种方式是抛出异常代表内存池溢出。
      根据以上的理念,boost的内存池分为四种。

    Pool

      Pool是一个Object Usage的内存池,溢出时返回NULL。

    object_pool

      object_pool与pool类似,唯一的区别是当其分配的内存释放时,它会尝试调用该对象的析购函数。

    singleton_pool

      singleton_pool是一个Singleton Usage的内存池,溢出时返回NULL。

    pool_alloc

      pool_alloc是一个Singleton Usage的内存池,溢出时抛出异常。

    内存池溢出的原理与解决方法

    必然溢出的内存

      内存池简化了很多内存方面的操作,也避免了一些错误使用内存对程序造成的损害。但是,使用内存池时最需要注意的一点是要处理内存池溢出的情况。
      没有不溢出的内存,看看下面的代码:

    #include "stdafx.h"
    #include <iostream>
    #include <ctime>
    #include <vector>
    #include <boost/pool/pool.hpp>
    #include <boost/pool/object_pool.hpp>
    using namespace std;
    using namespace boost;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        clock_t clock_begin = clock();
        int iLength = 0;
        for (int i = 0; ;++i)
        {
            void* p = malloc(1024*1024);
            if (p == NULL)
            {
                break;
            }
            ++iLength;
        }
        clock_t clock_end = clock();
        cout<<"共申请了"<<iLength<<"M内存,程序运行了"<<clock_end-clock_begin<<" 个系统时钟"<<endl;
        return 0;
    }

      运行的结果是“共申请了1916M内存,程序运行了 69421 个系统时钟”,意思是在分配了1916M内存后,malloc已经不能够申请到1M大小的内存块了。
      内存池在底层也是调用了malloc函数,因此内存池也是必然会溢出的。而且内存池可能会比直接调用malloc更早的溢出,看看下面的代码:

    #include "stdafx.h"
    #include <iostream>
    #include <ctime>
    #include <vector>
    #include <boost/pool/pool.hpp>
    #include <boost/pool/object_pool.hpp>
    using namespace std;
    using namespace boost;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        boost::pool<> pl(1024*1024);
        clock_t clock_begin = clock();
        int iLength = 0;
        for (int i = 0; ;++i)
        {
            void* p = pl.malloc();
            if (p == NULL)
            {
                break;
            }
            ++iLength;
        }
        clock_t clock_end = clock();
        cout<<"共申请了"<<iLength<<"M内存,程序运行了"<<clock_end-clock_begin<<" 个系统时钟"<<endl;
        return 0;
    }

      运行的结果是“共申请了992M内存,程序运行了 1265 个系统时钟”,意思是在分配了992M内存后,内存池已经不能够申请到1M大小的内存块了。

    内存池的基本原理

      从上面的两个测试可以看出内存池要比malloc溢出早,我的机器内存是1.5G,malloc分配了1916M才溢出(显然分配了虚拟内存),而内存池只分配了992M就溢出了。第二点是内存池溢出快,只用了1265微秒就溢出了,而malloc用了69421微秒才溢出。
      这些差别是内存池的处理机制造成的,内存池对于内存分配的算法如下,以pool内存池为例:
    - 1. pool初始化时带有一个块大小的参数memSize,那么pool刚开始会申请一大块内存,例如其大小为32*memSize。当然它还会申请一些空间用以管理链表,为方便述说,这里忽略这些内存。
    - 2. 用户不停的申请大小为memSize的内存,终于超过了内存池的大小,于是内存池启动重分配机制;
    - 3. 重分配机制会再申请一块大小为原内存池大小两倍的内存(那么第一次会申请64*memSize),然后将这块内存加到内存池的管理链表末尾;
    - 4. 用户继续申请内存,终于又一次超过了内存池的大小,于是又一次启动重分配机制,直至重分配时无法申请到新的内存块。
    - 5. 由于每次都是两倍于原内存,因此当内存池大小达到了992M时,再一次申请就需要1984M,但是malloc最多只能申请到1916M,因此malloc失败,内存池溢出。
      通过以上原理也可以理解为什么内存池溢出比malloc溢出要快得多,因为它是以2的指数级来扩大内存池,真正调用malloc的次数约等于log2(1916),而malloc是实实在在进行了1916次调用。所以内存池只用了1秒多就溢出了,而malloc用了69秒。

    内存池溢出的解决方法

      对于malloc造成的内存溢出,一般来说没有太多办法可想。基本上就是报一个异常或者错误,然后让用户关闭程序。当然有的程序会有内存自我管理功能,可以让用户选择关闭一切次要功能来维持主要功能的继续运行。
      而对于内存池的溢出,还是可以想一些办法的,因为毕竟系统内存还有潜力可挖。
      第一个方法是尽量延缓内存池的溢出,做法是在程序启动时就尽量申请最大的内存池,如果在程序运行很久后再申请,可能OS因为内存碎片增多而不能提供最大的内存池。其方法是在程序启动时就不停的申请内存直到内存池溢出,然后清空内存池并开始正常工作。由于内存池并不会自动减小,所以这样可以一直维持内存池保持最大状态。
      第二个方法是在内存池溢出时使用第二个内存池,由于第二个内存池可以继续申请较小块的内存,所以程序可继续运行。代码如下:

    #include "stdafx.h"
    #include <iostream>
    #include <ctime>
    #include <vector>
    #include <boost/pool/pool.hpp>
    #include <boost/pool/object_pool.hpp>
    using namespace std;
    using namespace boost;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        boost::pool<> pl(1024*1024);
        clock_t clock_begin = clock();
        int iLength = 0;
        for (int i = 0; ;++i)
        {
            void* p = pl.malloc();
            if (p == NULL)
            {
                break;
            }
            ++iLength;
        }
        clock_t clock_end = clock();
        cout<<"共申请了"<<iLength<<"M内存,程序运行了"<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        clock_begin = clock();
        iLength = 0;
        boost::pool<> pl2(1024*1024);
        for (int i = 0; ;++i)
        {
            void* p = pl2.malloc();
            if (p == NULL)
            {
                break;
            }
            ++iLength;
        }
        clock_end = clock();
        cout<<"又申请了"<<iLength<<"M内存,程序运行了"<<clock_end-clock_begin<<" 个系统时钟"<<endl;
    
        return 0;
    }

      运行结果如下:
    image.png

      结果表明在第一个内存池溢出后,第二个内存池又提供了480M的内存。

    内存池溢出的终极方案

      如果无论如何都不能再申请到新的内存了,那么还是老老实实告诉用户重启程序吧。

    展开全文
  • c++——内存池介绍

    2021-04-01 23:49:21
    系统在接收到分配一定大小内存的请求时,首先查找内部维护的内存空闲块表,并且需要根据一定的算法(例如分配最先找到的不小于申请大小的内存块给请求者,或者分配最适于申请大小的内存块,或者分配最大空闲的内存块...

    1.默认内存管理函数的不足

    利用默认的内存管理操作符new/delete和函数malloc()/free()在堆上分配和释放内存会有一些额外的开销。

    系统在接收到分配一定大小内存的请求时,首先查找内部维护的内存空闲块表,并且需要根据一定的算法(例如分配最先找到的不小于申请大小的内存块给请求者,或者分配最适于申请大小的内存块,或者分配最大空闲的内存块等)找到合适大小的空闲内存块。如果该空闲内存块过大,还需要切割成已分配的部分和较小的空闲块。然后系统更新内存空闲块表,完成一次内存分配。类似地,在释放内存时,系统把释放的内存块重新加入到空闲内存块表中。如果有可能的话,可以把相邻的空闲块合并成较大的空闲块。默认的内存管理函数还考虑到多线程的应用,需要在每次分配和释放内存时加锁,同样增加了开销。

    可见,如果应用程序频繁地在堆上分配和释放内存,会导致性能的损失。并且会使系统中出现大量的内存碎片,降低内存的利用率。默认的分配和释放内存算法自然也考虑了性能,然而这些内存管理算法的通用版本为了应付更复杂、更广泛的情况,需要做更多的额外工作。而对于某一个具体的应用程序来说,适合自身特定的内存分配释放模式的自定义内存池可以获得更好的性能。

    2.内存池简介

    2.1 内存池的定义

    内存池(Memory Pool)是一种内存分配方式。通常我们习惯直接使用new、malloc等API申请内存,这样做的缺点在于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。

    2.2 内存池的优点

    内存池则是在真正使用内存之前,预先申请分配一定数量、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。

    2.3 内存池的分类

    应用程序自定义的内存池根据不同的适用场景又有不同的类型。从线程安全的角度来分,内存池可以分为单线程内存池和多线程内存池。单线程内存池整个生命周期只被一个线程使用,因而不需要考虑互斥访问的问题;多线程内存池有可能被多个线程共享,因此需要在每次分配和释放内存时加锁。相对而言,单线程内存池性能更高,而多线程内存池适用范围更加广泛。

    从内存池可分配内存单元大小来分,可以分为固定内存池和可变内存池。所谓固定内存池是指应用程序每次从内存池中分配出来的内存单元大小事先已经确定,是固定不变的;而可变内存池则每次分配的内存单元大小可以按需变化,应用范围更广,而性能比固定内存池要低。

    3.经典的内存池技术

    内存池技术因为其对内存管理有着显著的优点,在各大项目中广泛应用,备受推崇。但是,通用的内存管理机制要考虑很多复杂的具体情况,如多线程安全等,难以对算法做有效的优化,所以,在一些特殊场合,实现特定应用环境的内存池在一定程度上能够提高内存管理的效率。

    经典内存池技术,是一种用于分配大量大小相同的小对象的技术。通过该技术可以极大加快内存分配/释放过程。既然是针对特定对象的内存池,所以内存池一般设置为类模板,根据不同的对象来进行实例化。

    3.1 经典内存池的设计

    3.1.1 经典内存池实现过程

    (1)先申请一块连续的内存空间,该段内存空间能够容纳一定数量的对象;
    (2)每个对象连同一个指向下一个对象的指针一起构成一个内存节点(Memory Node)。各个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间;
    (3)某个内存节点一旦分配出去,从空闲内存节点链表中去除;
    (4)一旦释放了某个内存节点的空间,又将该节点重新加入空闲内存节点链表;
    (5)如果一个内存块的所有内存节点分配完毕,若程序继续申请新的对象空间,则会再次申请一个内存块来容纳新的对象。新申请的内存块会加入内存块链表中。

    经典内存池的实现过程大致如上面所述,其形象化的过程如下图所示:

    如上图所示,申请的内存块存放三个可供分配的空闲节点。空闲节点由空闲节点链表管理,如果分配出去,将其从空闲节点链表删除,如果释放,将其重新插入到链表的头部。如果内存块中的空闲节点不够用,则重新申请内存块,申请的内存块由内存块链表来管理。

    注意,本文涉及到的内存块链表和空闲内存节点链表的插入,为了省去遍历链表查找尾节点,便于操作,新节点的插入均是插入到链表的头部,而非尾部。当然也可以插入到尾部,读者可自行实现。

    3.1.2 经典内存池数据结构设计

    按照上面的过程设计,内存池类模板有这样几个成员。

    两个指针变量:
    内存块链表头指针:pMemBlockHeader;
    空闲节点链表头指针:pFreeNodeHeader;

    空闲节点结构体:

    struct FreeNode
    {
    	FreeNode* pNext;
    	char data[ObjectSize];
    };
    

    内存块结构体:

    struct MemBlock
    {
    	MemBlock *pNext;
    	FreeNode data[NumofObjects];
    };
    

    3.2 经典内存池的实现

    根据以上经典内存池的设计,编码实现如下。

    #include <iostream>
    using namespace std;
    
    template<int ObjectSize, int NumofObjects = 20>
    class MemPool
    {
    private:
    	//空闲节点结构体
    	struct FreeNode
    	{
    		FreeNode* pNext;
    		char data[ObjectSize];
    	};
    
    	//内存块结构体
    	struct MemBlock
    	{
    		MemBlock* pNext;
    		FreeNode data[NumofObjects];
    	};
    
    	FreeNode* freeNodeHeader;
    	MemBlock* memBlockHeader;
    
    public:
    	MemPool()
    	{
    		freeNodeHeader = NULL;
    		memBlockHeader = NULL;
    	}
    
    	~MemPool()
    	{
    		MemBlock* ptr;
    		while (memBlockHeader)
    		{
    			ptr = memBlockHeader->pNext;
    			delete memBlockHeader;
    			memBlockHeader = ptr;
    		}
    	}
    	void* malloc();
    	void free(void*);
    };
    
    //分配空闲的节点
    template<int ObjectSize, int NumofObjects>
    void* MemPool<ObjectSize, NumofObjects>::malloc()
    {
    	//无空闲节点,申请新内存块
    	if (freeNodeHeader == NULL)
    	{
    		MemBlock* newBlock = new MemBlock;
    		newBlock->pNext = NULL;
    
    		freeNodeHeader=&newBlock->data[0];	 //设置内存块的第一个节点为空闲节点链表的首节点
    		//将内存块的其它节点串起来
    		for (int i = 1; i < NumofObjects; ++i)
    		{
    			newBlock->data[i - 1].pNext = &newBlock->data[i];
    		}
    		newBlock->data[NumofObjects - 1].pNext=NULL;
    
    		//首次申请内存块
    		if (memBlockHeader == NULL)
    		{
    			memBlockHeader = newBlock;
    		}
    		else
    		{
    			//将新内存块加入到内存块链表
    			newBlock->pNext = memBlockHeader;
    			memBlockHeader = newBlock;
    		}
    	}
    	//返回空节点闲链表的第一个节点
    	void* freeNode = freeNodeHeader;
    	freeNodeHeader = freeNodeHeader->pNext;
    	return freeNode;
    }
    
    //释放已经分配的节点
    template<int ObjectSize, int NumofObjects>
    void MemPool<ObjectSize, NumofObjects>::free(void* p)
    {
    	FreeNode* pNode = (FreeNode*)p;
    	pNode->pNext = freeNodeHeader;	//将释放的节点插入空闲节点头部
    	freeNodeHeader = pNode;
    }
    
    class ActualClass
    {
    	static int count;
    	int No;
    
    public:
    	ActualClass()
    	{
    		No = count;
    		count++;
    	}
    
    	void print()
    	{
    		cout << this << ": ";
    		cout << "the " << No << "th object" << endl;
    	}
    
    	void* operator new(size_t size);
    	void operator delete(void* p);
    };
    
    //定义内存池对象
    MemPool<sizeof(ActualClass), 2> mp;
    
    void* ActualClass::operator new(size_t size)
    {
    	return mp.malloc();
    }
    
    void ActualClass::operator delete(void* p)
    {
    	mp.free(p);
    }
    
    int ActualClass::count = 0;
    
    int main()
    {
    	ActualClass* p1 = new ActualClass;
    	p1->print();
    
    	ActualClass* p2 = new ActualClass;
    	p2->print();
    	delete p1;
    
    	p1 = new ActualClass;
    	p1->print();
    
    	ActualClass* p3 = new ActualClass;
    	p3->print();
    
    	delete p1;
    	delete p2;
    	delete p3;
    }
    

    3.3 程序分析

    阅读以上程序,应注意以下几点。
    (1)对一种特定的类对象而言,内存池中内存块的大小是固定的,内存节点的大小也是固定的。内存块在申请之初就被划分为多个内存节点,每个 Node 的大小为 ItemSize。刚开始,所有的内存节点都是空闲的,被串成链表。

    (2)成员指针变量 memBlockHeader 是用来把所有申请的内存块连接成一个内存块链表,以便通过它可以释放所有申请的内存。freeNodeHeader 变量则是把所有空闲内存节点串成一个链表。freeNodeHeader为空则表明没有可用的空闲内存节点,必须申请新的内存块。

    (3)申请空间的过程如下。在空闲内存节点链表非空的情况下,malloc 过程只是从链表中取下空闲内存节点链表的头一个节点,然后把链表头指针移动到下一个节点上去。否则,意味着需要一个新的内存块。这个过程需要申请新的内存块切割成多个内存节点,并把它们串起来,内存池技术的主要开销就在这里。

    (4)释放对象的过程就是把被释放的内存节点重新插入到内存节点链表的开头。最后被释放的节点就是下一个即将被分配的节点。

    (5)内存池技术申请/释放内存的速度很快,其内存分配过程多数情况下复杂度为 O(1),主要开销在 freeNodeHeader 为空时需要生成新的内存块。内存节点释放过程复杂度为 O(1)。

    (6) 在上面的程序中,指针 p1 和 p2 连续两次申请空间,它们代表的地址之间的差值为 8,正好为一个内存节点的大小(sizeof(FreeNode))。指针 p1 所指向的对象被释放后,再次申请空间,得到的地址与刚刚释放的地址正好相同。指针 p3 多代表的地址与前两个对象的地址相聚很远,原因是第一个内存块中的空闲内存节点已经分配完了,p3 指向的对象位于第二个内存块中。

    以上内存池方案并不完美,比如,只能单个单个申请对象空间,不能申请对象数组,内存池中内存块的个数只能增大不能减少,未考虑多线程安全等问题。现在,已经有很多改进的方案,请读者自行查阅相关资料。

    展开全文
  • 课程简介 C++ 11 14 17 20内存管理-指针、智能指针和内存池从基础到实战 学习计划 1. 每天学习一小时以上 2. 跟着视频动手编写代码 3. 调试代码并对比课程多提供的源码 课程目标 1. 理解指针...
  • refpool对Rust的std :: boxed :: Box和std :: rc :: Rc的重新实现,它使用可重用内存池来加快重新分配的速度。 快吗? 这是一个refpool。Rust的std :: boxed :: Box和std :: rc :: Rc的重新实现,它使用可重用内存池...
  • linux下内存池实现

    2016-01-29 20:14:50
    适用于linux服务器开发,提升性能,希望对你有帮助!
  • 一个C++内存池实现

    2015-12-13 22:35:22
    一个内存池实现,包括一个全局的一级内存池和每个线程单独一个的二级内存池.每个分配的块会有一个指针大小的内存浪费,希望可以有人帮忙改进,
  • 内存池是一系列固定大小的内存空间,每一个内存池主要包括很多内存单元(具体的存储区域)和内存控制单元(控制对应的内存单元),每一个内存单元大小相同,但是具体的大小依据需要设计。而控制单元主要是实现每一个...
  • 内存池组件以及根据nginx内存池源码设计实现简易内存池

    目录

     造轮子内存池原因引入 

    大量的malloc/free小内存所带来的弊端

    弊端

    出现场景

    大牛解决措施(nginx内存池)  

    内存池技术

    啥叫作内存池技术

    内存池技术为啥可以解决上文弊端

    高并发内存池nginx内存池源码刨析

    啥是高并发

    nginx_memory_pool为啥就适合高并发

    仿写nginx内存池

    实现思路

     内存池大小, 以及内存对齐的宏定义​编辑

    结构定义以及图解分析

    函数原型以及功能叙述

    重点函数分块细节刨析

    mp_create_pool: 创建线程池

    mp_alloc 带字节对齐的内存申请

    mp_alloc_block 申请创建新的小块内存

    mp_alloc_large 申请创建新的大块内存

     mp_free_large 回收大块内存资源

    整体代码附下


     造轮子内存池原因引入 

    作为C/C++程序员, 相较JAVA程序员的一个重大特征是我们可以直接访问内存, 自己管理内存, 这个可以说是我们的特色, 也是我们的苦楚了.

    java可以有虚拟机帮助管理内存, 但是我们只能自己管理内存, 一不小心产生了内存泄漏问题, 又特别是服务器的内存泄漏问题, 进程不死去, 泄漏的内存就一直无法回收.

    所以对于内存的管理一直是我们C系列程序员深挖的事情. 

    所以对于C++有智能指针这个东西. 还有内存池组件. 内存池组件也不能完全避免内存泄漏, 但是它可以很好的帮助我们定位内存泄漏的点, 以及可以减少内存申请和释放的次数, 提高效率

    大量的malloc/free小内存所带来的弊端

    弊端

    1. malloc/free的底层是调用系统调用, 这两者库函数是对于系统调用的封装, 频繁的系统调用所带来的用户内核态切换花费大量时间, 大大降低系统执行效率
    2. 频繁的申请小内存, 带来的大量内存碎片, 内存使用率低下且导致无法申请大块的内存
    3. 没有内存回收机制, 很容易造成内存泄漏

    内存碎片出现原因解释

    • 内部内存碎片定义:  已经被分配出去了(明确分配到一个进程), 但是无法被利用的空间 
    • 内存分配的起始地址 一定要是 4, 8, 16整除地址
    • 内存是按照页进行分配的, 中间会产生外部内存碎片, 无法分配给进程
    • 内部内存碎片:频繁的申请小块内存导致了内存不连续性,中间的小内存间隙又不足以满足我们的内存申请要求, 无法申请出去利用起来, 这个就是内部内存碎片.

    出现场景

    最为典型的场景就是高并发是的频繁内存申请, 释放. (http请求) (tcp连接)

    大牛解决措施(nginx内存池)  

    nginx内存池, 公认的设计方式非常巧妙的一款内存池设计组件, 专门针对高并发下面的大量的内存申请释放而产生的. 

    在系统层,我们可以使用高性能内存管理组件 Tcmalloc Jemalloc(优化效率和碎片问题)

    在应用层: 我们可以根据需求设计内存池进行管理  (高并发可以借助nginx内存池设计)

    内存池技术

    啥叫作内存池技术

    就是说在真正使用内存之前, 先提前申请分配一定数量的、大小相等(一般情况下)的内存块留作备用, 当需要分配内存的时候, 直接从内存块中获取. 如果内存块不够了, 再申请新的内存块.

    内存池: 就是将这些提前申请的内存块组织管理起来的数据结构

    优势何在:统一对程序所使用的内存进行统一的分配和回收, 提前申请的块, 然后将块中的内存合理的分配出去, 极大的减少了系统调用的次数. 提高了内存利用率.  统一的内存分配回收使得内存泄漏出现的概率大大降低

    内存池技术为啥可以解决上文弊端

    高并发时系统调用频繁(malloc free频繁),降低了系统的执行效率

    • 内存池提前预先分配大块内存,统一释放,极大的减少了malloc 和 free 等函数的调用。

    频繁使用时增加了系统内存的碎片,降低内存使用效率

    • 内存池每次请求分配大小适度的内存块,最大避免了碎片的产生

    没有内存回收机制,容易造成内存泄漏

    • 在生命周期结束后统一释放内存,极大的避免了内存泄露的发生

    高并发内存池nginx内存池源码刨析

    啥是高并发

    系统能够同时并行处理很多请求就是高并发

    高并发具备的特征

    • 响应时间短
    • 支持并发用户数高
    • 支持用户接入量高
    • 连接建立时间短

    nginx_memory_pool为啥就适合高并发

    内存池生存时间应该尽可能短,与请求或者连接具有相同的周期

    减少碎片堆积和内存泄漏

    避免不同请求连接之间互相影响

    一个连接或者一个请求就创建一个内存池专门为其服务, 内存池的生命周期和连接的生命周期保持一致. 

    仿写nginx内存池

    实现思路

    • 对于每个请求或者连接都会建立相应的内存池,建立好内存池之后,我们可以直接从内存池中申请所需要的内存,不用去管内存的释放,当内存池使用完成之后一次性销毁内存池。
    • 区分大小内存块的申请和释放,大于内存池块最大尺寸的定义为大内存块,使用单独的大内存块链表保存,即时分配和释放
    • 小于等于池尺寸的定义为小内存块,直接从预先分配的内存块中提取,不够就扩充池中的内存,在生命周期内对小块内存不做释放,直到最后统一销毁。

     内存池大小, 以及内存对齐的宏定义

    #define MP_ALIGNMENT       		32
    #define MP_PAGE_SIZE			4096
    #define MP_MAX_ALLOC_FROM_POOL	(MP_PAGE_SIZE-1)
    
    #define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))
    //分配内存起点对齐

    结构定义以及图解分析

    typedef struct mp_large_s {
        struct mp_large_s* next;
        void* alloc;//data区
    } mp_large_s;
    
    typedef struct mp_node_s {
        unsigned char* last;//下一次内存分配的起点
        unsigned char* end;//当前内存块末尾
        size_t failed;//当前内存块分配失败的次数
        struct mp_node_s* next;
    } mp_node_s;
    
    typedef struct mp_pool_s {
        mp_large_s* large;//指向大块内存起点
        mp_node_s* current;//指向当前可分配的小内存块起点
        int max;//小块最大内存
        mp_node_s head[0];//存储地址, 不占据内存,变长结构体技巧
        //存储首块小内存块head地址
    } mp_pool_s;

    mp_pool_s     内存池结构

    1. large       指向第一个大块
    2. current   指向当前可分配的小块
    3. head       始终指向第一块小块

    mp_node_s     小块内存结构

    1. last         下一次内存分配的起点, 本次内存分配的终点
    2. end         块内存末尾
    3. failed      当前内存块申请内存的失败次数, nginx采取的方式是失败次数达到一定程度就更换current,current是开始尝试分配的内存块, 也就是说失败达到一定次数, 就不再申请这个内存块了.

    mp_large_s        大块内存块

    1. 正常的申请, 然后使用链表连接管理起来.
    2. alloc           内存块, 分配内存块 

    函数原型以及功能叙述

    //函数申明
    mp_pool_s *mp_create_pool(size_t size);//创建内存池
    void mp_destory_pool( mp_pool_s *pool);//销毁内存池
    void *mp_alloc(mp_pool_s *pool, size_t size);
    //从内存池中申请并且进行字节对齐
    void *mp_nalloc(mp_pool_s *pool, size_t size);
    //从内存池中申请不进行字节对齐
    void *mp_calloc(mp_pool_s *pool, size_t size);
    //模拟calloc
    void mp_free(mp_pool_s *pool, void *p);
    void mp_reset_pool(struct mp_pool_s *pool);
    //重置内存池
    static void *mp_alloc_block(struct mp_pool_s *pool, size_t size);
    //申请小块内存
    static void *mp_alloc_large(struct mp_pool_s *pool, size_t size);
    //申请大块内存

    对应nginx函数原型

    重点函数分块细节刨析

    mp_create_pool: 创建线程池

    第一块内存: 大小设置为  size + sizeof(node) + sizeof(pool) ?

    mp_node_s head[0] 啥意思?

    mp_pool_s* mp_create_pool(size_t size) {
        struct mp_pool_s *p = NULL;
    	int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(mp_pool_s) + sizeof(mp_node_s));
    	if (ret) {
    		return NULL;
    	}
    	//内存池小块的大小限制
    	p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
    	p->current = p->head;//第一块为当前块
    	p->large = NULL;
    
    	p->head->last = (unsigned char *)p + sizeof( mp_pool_s) + sizeof(mp_node_s);
    	p->head->end = p->head->last + size;
    	p->head->failed = 0;
    
    	return p;
    }

    看完了代码来回答一下问题

    1. 为了尽可能地避免内存碎片地产生, 小内存地申请, 于是我采取地方式是将 memory pool内存池也放入到首块内存中地方式. 同时所有地node结点信息也都统一存储在每一个内存块中.
    2. head[0] : 是一种常用于变长结构体地技巧, 不占用内存, 仅仅只是表示一个地址信息, 存储head node 的地址. 

    mp_alloc 带字节对齐的内存申请

    首先按照size大小选择内存分配方式, 小于等于线程池小块最大大小限制就从已有小块中申请, 小块不足就调用mp_alloc_block创建新的小块   否则就调用 mp_alloc_large 申请创建一个大块内存

    mp_align_ptr 用于字节对齐

    void *mp_alloc(mp_pool_s *pool, size_t size) {
        mp_node_s* p = NULL;
        unsigned char* m = NULL;
        if (size <= MP_MAX_ALLOC_FROM_POOL) {//从小块中分配
            p = pool->current;
            do {//循环尝试从现有小块中申请
                m = mp_align_ptr(p->last, MP_ALIGNMENT);
                if ((size_t)(p->end - m) >= size) {
                    p->last = m + size;
                    return m;  
                }
                p = p->next;
            } while (p);
            //说明小块中都分配失败了, 于是从新申请一个小块
            return mp_alloc_block(pool, size);
        }
        //从大块中分配
        return mp_alloc_large(pool, size);
    }

    mp_alloc_block 申请创建新的小块内存

    psize 大小等于mp_node_s结点内存大小 +  实际可用内存块大小

    搞清楚内存块组成:结点信息 + 实际可用内存块

    返回的内存是实际可用内存的起始地址

    //申请小块内存
    void *mp_alloc_block(struct mp_pool_s *pool, size_t size) {
        unsigned char* m = NULL;
        size_t psize = 0;//内存池每一块的大小
        psize = (size_t)((unsigned char*)pool->head->end - (unsigned char*)pool->head);
        int ret = posix_memalign((void**)&m, MP_ALIGNMENT, psize);
        if (ret) return NULL;
        //此时已经分配出来一个新的块了
        mp_node_s* new_node, *p, *current;
        new_node = (mp_node_s*)m;
    
        new_node->end = m + psize;
        new_node->failed = 0;
        new_node->next = NULL;
    
        m += sizeof(mp_node_s);//跳过node
        //对于m进行地址起点内存对齐
        m = mp_align_ptr(m, MP_ALIGNMENT);
        new_node->last = m + size;
        
        current = pool->current;
        //循环寻找新的可分配内存块起点current
        for (p = current; p->next; p = p->next) {
            if (p->failed++ > 4) {
                current = p->next;
            }
        }
        //将new_node连接到最后一块内存上, 并且尝试跟新pool->current
        pool->current = current ? current : new_node;
        p->next = new_node;
        return m;
    }

    mp_alloc_large 申请创建新的大块内存

    大块内存参考nginx_pool 采取采取的是malloc分配

    先分配出来所需大块内存. 在pool的large链表中寻找是否存在空闲的alloc. 存在则将内存挂在上面返回.  寻找5次还没有找到就另外申请一个新的large结点挂载内存, 链接到large list中管理

    mp_large_s* node 是从内存池中分配的, 也就是从小块中分配的 why? 减少内存碎片, 将大块的node信息放入小块内存中,避免小内存的申请, 减少内存碎片

    留疑? 空闲的alloc从何而来?

    void *mp_alloc_large(struct mp_pool_s *pool, size_t size) {
        void* p = malloc(size);
        if (p == NULL) return NULL;
        mp_large_s* l = NULL;
        size_t cnt = 0;
    
        for (l = pool->large; l; l = l->next) {
            if (l->alloc) {
                l->alloc = p;
                return p;
            }
            if (cnt++ > 3) {
                break;//为了提高效率, 检查前5个块, 没有空闲alloc就从新申请large
            }
        }
     	l = mp_alloc(pool, sizeof(struct mp_large_s));
    	if (l == NULL) {
    		free(p);
    		return NULL;
    	}
        
        l->alloc = p;
        l->next = pool->large;
        pool->large = l;
        return p;
    }

    空闲的alloc是被free掉了空闲出来的.   虽然nginx采取的是小块不单独回收, 最后统一回收, 因为小块的回收非常难以控制, 不清楚何时可以回收. 但是对于大块nginx提供了free回收接口. 

     mp_free_large 回收大块内存资源

    void mp_free_large(mp_pool_s *pool, void *p) {
        mp_large_s* l = NULL;
        for (l = pool->large; l; l = l->next) {
            if (p == l->alloc) {
                free(l->alloc);
    
                l->alloc = NULL;
                return ;
            }
        }
    
    }

    整体代码附下

    #ifndef _MPOOL_H_
    #define _MPOOL_H_
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    #define MP_ALIGNMENT       		32
    #define MP_PAGE_SIZE			4096
    #define MP_MAX_ALLOC_FROM_POOL	(MP_PAGE_SIZE-1)
    
    #define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))
    //内存起点对齐
    
    typedef struct mp_large_s {
        struct mp_large_s* next;
        void* alloc;//data区
    } mp_large_s;
    
    typedef struct mp_node_s {
        unsigned char* last;//下一次内存分配的起点
        unsigned char* end;//当前内存块末尾
        size_t failed;//当前内存块分配失败的次数
        struct mp_node_s* next;
    } mp_node_s;
    
    typedef struct mp_pool_s {
        mp_large_s* large;//指向大块内存起点
        mp_node_s* current;//指向当前可分配的小内存块起点
        int max;//小块最大内存
        mp_node_s head[0];//存储地址, 不占据内存,变长结构体技巧
        //存储首块小内存块head地址
    } mp_pool_s;
    
    //函数申明
    mp_pool_s *mp_create_pool(size_t size);//创建内存池
    void mp_destory_pool( mp_pool_s *pool);//销毁内存池
    void *mp_alloc(mp_pool_s *pool, size_t size);
    //从内存池中申请并且进行字节对齐
    void *mp_nalloc(mp_pool_s *pool, size_t size);
    //从内存池中申请不进行字节对齐
    void *mp_calloc(mp_pool_s *pool, size_t size);
    //模拟calloc
    void mp_free(mp_pool_s *pool, void *p);
    void mp_reset_pool(struct mp_pool_s *pool);
    //重置内存池
    static void *mp_alloc_block(struct mp_pool_s *pool, size_t size);
    //申请小块内存
    static void *mp_alloc_large(struct mp_pool_s *pool, size_t size);
    //申请大块内存
    
    mp_pool_s* mp_create_pool(size_t size) {
        struct mp_pool_s *p = NULL;
    	int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(mp_pool_s) + sizeof(mp_node_s));
    	if (ret) {
    		return NULL;
    	}
    	//内存池小块的大小限制
    	p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
    	p->current = p->head;//第一块为当前块
    	p->large = NULL;
    
    	p->head->last = (unsigned char *)p + sizeof( mp_pool_s) + sizeof(mp_node_s);
    	p->head->end = p->head->last + size;
    	p->head->failed = 0;
    
    	return p;
    }
    
    void mp_destory_pool( mp_pool_s *pool) {
        //先销毁大块
        mp_large_s* l = NULL;
        mp_node_s* p = pool->head->next, *q = NULL;
        for (l = pool->large; l; l = l->next) {
            if (l->alloc) {
                free(l->alloc);
                l->alloc = NULL;
            }
        }
        //然后销毁小块内存
        while (p) {
            q = p->next;
            free(p);
            p = q;
        }
        free(pool);
    }
    
    
    
    //申请小块内存
    void *mp_alloc_block(struct mp_pool_s *pool, size_t size) {
        unsigned char* m = NULL;
        size_t psize = 0;//内存池每一块的大小
        psize = (size_t)((unsigned char*)pool->head->end - (unsigned char*)pool->head);
        int ret = posix_memalign((void**)&m, MP_ALIGNMENT, psize);
        if (ret) return NULL;
        //此时已经分配出来一个新的块了
        mp_node_s* new_node, *p, *current;
        new_node = (mp_node_s*)m;
    
        new_node->end = m + psize;
        new_node->failed = 0;
        new_node->next = NULL;
    
        m += sizeof(mp_node_s);//跳过node
        //对于m进行地址起点内存对齐
        m = mp_align_ptr(m, MP_ALIGNMENT);
        new_node->last = m + size;
        current = pool->current;
        for (p = current; p->next; p = p->next) {
            if (p->failed++ > 4) {
                current = p->next;
            }
        }
        //将new_node连接到最后一块内存上, 并且尝试跟新pool->current
        pool->current = current ? current : new_node;
        p->next = new_node;
        return m;
    }
    
    //申请大块内存
    void *mp_alloc_large(struct mp_pool_s *pool, size_t size) {
        void* p = malloc(size);
        if (p == NULL) return NULL;
        mp_large_s* l = NULL;
        size_t cnt = 0;
    
        for (l = pool->large; l; l = l->next) {
            if (l->alloc) {
                l->alloc = p;
                return p;
            }
            if (cnt++ > 3) {
                break;//为了提高效率, 检查前5个块, 没有空闲alloc就从新申请large
            }
        }
     	l = mp_alloc(pool, sizeof(struct mp_large_s));
    	if (l == NULL) {
    		free(p);
    		return NULL;
    	}
        
        l->alloc = p;
        l->next = pool->large;
        pool->large = l;
        return p;
    }
    
    
    //带有字节对齐的申请
    void *mp_alloc(mp_pool_s *pool, size_t size) {
        mp_node_s* p = NULL;
        unsigned char* m = NULL;
        if (size < MP_MAX_ALLOC_FROM_POOL) {//从小块中分配
            p = pool->current;
            do {
                m = mp_align_ptr(p->last, MP_ALIGNMENT);
                if ((size_t)(p->end - m) >= size) {
                    p->last = m + size;
                    return m;  
                }
                p = p->next;
            } while (p);
            //说明小块中都分配失败了, 于是从新申请一个小块
            return mp_alloc_block(pool, size);
        }
        //从大块中分配
        return mp_alloc_large(pool, size);
    }
    
    
    
    //不带字节对齐的从内存池中申请内存
    void *mp_nalloc(mp_pool_s *pool, size_t size) {
        mp_node_s* p = NULL;
        unsigned char* m = NULL;
        if (size < MP_MAX_ALLOC_FROM_POOL) {//从小块中分配
            p = pool->current;
            do {
                m = p->last;
                if ((size_t)(p->end - m) >= size) {
                    p->last = m + size;
                    return m;  
                }
                p = p->next;
            } while (p);
            //说明小块中都分配失败了, 于是从新申请一个小块
            return mp_alloc_block(pool, size);
        }
        //从大块中分配
        return mp_alloc_large(pool, size);
    }
    
    
    void *mp_calloc(struct mp_pool_s *pool, size_t size) {
    
    	void *p = mp_alloc(pool, size);
    	if (p) {
    		memset(p, 0, size);
    	}
    
    	return p;
    	
    }
    
    void mp_free(mp_pool_s *pool, void *p) {
        mp_large_s* l = NULL;
        for (l = pool->large; l; l = l->next) {
            if (p == l->alloc) {
                free(l->alloc);
    
                l->alloc = NULL;
                return ;
            }
        }
    
    }
    
    #endif 
    

    展开全文
  • 前面已经讲了一些C中基本的内存管理的方式,...  我们使用如下的数据结构来管理内存池:  struct T {  T prev;  char *avail;  char *limit;  };  prev指向存储块的头, avail指向第一个可用地址,limit指
  • 内存池中内存块获取、分配机制、内存块大小、内存释放,以及在多线程环境下的安全处理等细节进行了研究,保证了在多线程环境下能够快速同时采用一种基于数组的链表机制,改进内存池中内存块的查找算法,将其时间...
  • 需要说明的就是初始化的时候指定的是内存池分配内存块的大小,而且内存池是非线程安全的,多线程要加锁 #ifndef BUFFER_POOL_H #define BUFFER_POOL_H #include "boost_comm.h" using namespace boost; namespace...
  • 不定长内存池算法

    2018-04-08 12:12:05
    c++实现的不定长内存池算法,效率较高,能较好重载new delete时使用,以及配合其他开源库需要的内存分配算法使用
  • C语言内存池

    2018-08-30 11:05:06
    自己实现的C语言内存池,代码量少,支持自定义内存池数量,大小。使用非常简单,支持多线程
  • c++高性能内存池.rar

    2020-03-27 14:12:48
    std::allocator 是 C++标准库中提供的默认分配器,他的特点就在于我们在 使用 new 来申请内存构造新对象的时候,势必要调用类对象的默认构造函数 ...所以为了让代码直接可用,我们同样应该在内存池中设计同样的接口:
  • 内存池作用:(内存池但不要自己造,要懂他的原理) 避免频繁分配内存(recv之前malloc一块空间,让recv函数来,这种方式内存会被频繁分配释放和使用,会造成内存碎片化,这样当我们再次申请一块大内存的时候...
  • APACHE内存池概述.pdf

    2020-05-09 09:54:44
    APACHE内存池概述.pdf
  • c/c++内存池

    2017-09-26 22:08:44
    c/c++内存池 线程安全的c/c++内存池 线程安全的c/c++内存池 线程安全的c/c++内存池 线程安全的c/c++内存池 线程安全的c/c++内存池 线程安全的c/c++内存池 线程安全的c/c++内存池 线程安全的c/c++内存池 线程安全的c/...
  • mysql内存池.zip

    2019-08-07 15:38:37
    mysql的内存池用了自己的伙伴算法给予实现,因mysql采用C语言开发不便查看,特将封装为C++一个类。可以直接用于其他项目,减少内存碎片。
  • 不善言谈,身边缺少技术大牛交谈,项目缺少使用技术机会,主动知道知识,...但有一次面试,面试官问到内存池的实现原理,如果基于内存池用new对对象进行内存申请如何实现? ===》 第一次思考这个问题,当时却懵了,感
  • 1 为什么需要内存池 (1)避免频繁分配内存;副作用:造成内存的碎片;长时间运行会内存分配失败。 (2)解决频繁分配内存,采用内存池。 (3)jemalloc,tcmalloc开源框架 2 内存池的原理 (1)链表组织,每次分配...
  • 比较使用内存池与不用内存池的不同性能。 结果:在Windows上使用内存池会大大提高性能,但在Linux中却没有显著提高。
  • C++内存池设计

    2021-09-16 11:06:18
    一、什么是内存池?...内存数据也可以,为什么还要使用内存池? 如果是通过消息队列,把数据push到另外一个线程处理业务的时候,定义的数组是不了的。此时我们就可以使用内存池传递指针过去。 ..
  • windows 内存池代码参考
  • Nginx 内存池剖析

    千次阅读 2022-03-06 19:03:05
    Nginx 内存池剖析为什么要使用 Nginx 内存池传统直接调用内存分配函数的弊端弊端的解决之道什么是Nginx 内存池什么是内存池技术内存池如何解决弊端内存池的设计思想分而治之Nginx 内存池结构体图内存池模块部分源...
  • 本篇文章对一个简单定长内存池的实现方法进行了详细的分析介绍。需要的朋友参考下

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 445,243
精华内容 178,097
关键字:

内存池怎么用