前阵子看了一下网狐的内存池的实现,感觉这个内存池的实现简单明了,高效,数据包的加入与取出类似队列的原理,先进先出。不足之处是当在一段连续时间内只有数据放入内存池的动作,而且没有从内存池取出数据的动作,内存池中申请的内存不断的增大,每次申请更大内存时,就出现内存数据的拷贝,数据量越大,拷贝的内存数据量也就越大。所以这个内存池的实现应该比较适合生产者(产生数据放入内存池) 加入数据速率与 消费者(从内存池中取出数据进行后续处理)取出数据速率相对比较均衡的情况下,这样就能以占用最小的内存量达到最大的吐吞率,因为内存池的最大量会达到一定上限,不会不断扩大,并且减少了内存碎片。

下面具体分析一下这个内存池的基本实现原理,具体实现代码不贴出来,本文只是对实现原理进行分析。首先看一下内存池类CDataStorage 的主要成员变量:

DWORD    m_dwDataSize;                                    //内存池中数据的大小

DWORD    m_dwBufferSize;                                 //内存池的总大小

DWORD    m_dwInsertPos;                                  //当前数据加入的位置

DWORD    m_dwTerminalPos;                              //当前数据的终点位置

DWORD    m_dwDataQueryPos;                           //当前取出的数据位置

DWORD    m_dwDataPacketCount;                       //内存池中数据包数是

BYTE        * m_pDataStorageBuffer;                       //指向内存块的指针

下面分步对内存池的使用进行讨论:

1. 初始时,内存池的空间为0,

     m_dwDataSize=0;

       m_dwBufferSize=0;

       m_dwInsertPos=0;

       m_dwTerminalPos=0;

       m_dwDataQueryPos=0;

       m_dwDataPacketCount=0;

       m_pDataStorageBuffer=NULL;

2. 假设有生产者把1K数据包加入内存池时,这时在堆上申请10K的大小的内存块。申请 10K是为了避免后面需要更大一点内存时,频繁的申请内存,降低效率和造成大量的内存碎片,当内存池不够时,会再原来的内存大小基础上申请更大的内存。

此时内存池如下:

       m_dwDataSize= 1K;

            m_dwBufferSize=10K;

           m_dwInsertPos=1K;

           m_dwTerminalPos=1K;

           m_dwDataQueryPos=0;

           m_dwDataPacketCount=1;

 

3. 消费者取出1K数据包, 这时各变量值如下:

    m_dwDataSize= 0;

    m_dwBufferSize=10K;

    m_dwInsertPos=1K;

    m_dwTerminalPos=1K;

    m_dwDataQueryPos=1K;

    m_dwDataPacketCount=0

 

4. 生产者放入2K的数据包,此时内存池情况如下:

    m_dwDataSize= 2K;

    m_dwBufferSize=10K;

    m_dwInsertPos= 3K;

    m_dwTerminalPos=3K;

    m_dwDataQueryPos=1K;

    m_dwDataPacketCount=1

 

5.生产者 再把11K的数据包放入内存池,由于内存池大小只有10K,且已经用了2K,所以这时申请更大的内存,假设申请20K,然后,把原来内存池的数据拷贝新申请的内存池中,到再把数据放入新内存中并释放掉旧的内存池,此时内存池情况如下:

    m_dwDataSize= 13K;

    m_dwBufferSize=20K;

    m_dwInsertPos= 13K;

    m_dwTerminalPos=23K;

    m_dwDataQueryPos=0K;

    m_dwDataPacketCount=2

 

6 消费者取出2K的数据包后,此时内存池情况如下:

    m_dwDataSize= 11K;

    m_dwBufferSize=20K;

    m_dwInsertPos= 13K;

    m_dwTerminalPos=13K;

    m_dwDataQueryPos=2K;

    m_dwDataPacketCount=1

 

7.生产者再放入8K的数据包,此时,内存池总共还有9K空闲内存,但是没有连续在一起 ,这时需要先移动内存,整理出足够的连续空间,再放入8K的数据包。

此时内存池情况如下:

    m_dwDataSize= 19K;

    m_dwBufferSize=20K;

    m_dwInsertPos= 19K;

    m_dwTerminalPos=19K;

    m_dwDataQueryPos=0;

   m_dwDataPacketCount=2

 

8.消费者再取出11K数据包后,此时内存池情况如下:

    m_dwDataSize= 8K;

    m_dwBufferSize=20K;

    m_dwInsertPos= 19K;

    m_dwTerminalPos=19K;

    m_dwDataQueryPos=11K;

    m_dwDataPacketCount=1

 

9. 生产者此时再要放入 9K的数据包时,此时内存池最前面有足够的空闲空间,如下:

    m_dwDataSize= 17K;

    m_dwBufferSize=20K;

    m_dwInsertPos= 9K;

    m_dwTerminalPos=19K;

    m_dwDataQueryPos=11K;

    m_dwDataPacketCount=2

 

10.  生产者再放入 2.5K的数据包 此时总共空闲空间足够大,但不连续,并且存在两个分离的数据包,数据包的取出是先进先出,所以必须再重新申请一块更大的连续空间,比如40K,并进行数据拷贝,释放旧内存池.

    m_dwDataSize= 19.5K;

    m_dwBufferSize=40K;

    m_dwInsertPos= 19.5K;

    m_dwTerminalPos=19.5K;

    m_dwDataQueryPos=0;

    m_dwDataPacketCount=3

 

以上10个步骤,基本上体现对内存池操作动作。可以看出,如果取出与加入数据包的速度一直保持相当的话,这个内存池可以保持占用较小必要的内存。因为只要有更大的需要,就会不断的申请内存,内存池只会不断增大,不会减小,直接进程结束才释放掉。如果某个短时间加入数据包操作很多,但没取出数据包,这时会申请的很大的内存,等数据包取出完后,后面的时间里取出与加入数据包的速度相当的话,那么就只是一直使用一点点内存的点,从而造成内存空间的浪费,这个模式就不适合这种情况了,不过,我想这种情况应该是很少的。

 posted on 2010-08-15 15:30  kundij  阅读(1907)  评论(0编辑  收藏  举报