精华内容
下载资源
问答
  • 目录1 为什么使用mysql内存池2 mysql内存池的基本用法3 mysql内存池如何工作的4 两个最常用的使用场景5 mysql内存池的代码说明6一个简单的例子1 为什么使用mysql内存池可以简化程序内存的管理. Mysql内部使用的就是...

    目录

    1 为什么使用mysql内存池

    2 mysql内存池的基本用法

    3 mysql内存池如何工作的

    4 两个最常用的使用场景

    5 mysql内存池的代码说明

    6一个简单的例子

    1 为什么使用mysql内存池

    可以简化程序内存的管理. Mysql内部使用的就是它. 可以实现多次申请内存块, 中途任何时刻失败, 或者下次使用前释放内存, 无需再关心每次申请和释放了哪些内存.

    2 mysql内存池的基本用法

    主要使用这3个函数(其他函数请查看my_alloc.h):

    2.1 初始化内存池:

    void init_alloc_root(MEM_ROOT *mem_root, size_t block_size, size_t pre_alloc_size __attribute__((unused)));

    block_size 申请的内存块最小内存

    使用实例:

    MEM_ROOT JsonMemRoot;

    init_alloc_root(&JsonMemRoot, 1<<12, 0);

    2.2 在内存池中申请使用一块内存

    void * alloc_root(MEM_ROOT *mem_root, size_t length);

    length 申请的内存块内存, 如果前面申请的没有用完, 将使用前面的.

    如果已经用完了将申请新的, 如果新申请的不能被sizeof(double) 整除, 则增大它使之整除(内存对齐).

    取length和init_alloc_root中的block_size, 其中的较大值申请新的内存

    使用实例:

    static int TestMemRoot()

    {

    char * str = (char *) alloc_root(&JsonMemRoot, 1024 * sizeof(char));

    if (NULL == str)

    {

    WriteLogNF("TestMemRoot : cannot alloc_root for str\\n");

    return -1;

    }

    return 0;

    }

    2.3 释放内存池

    (MyFlags : MY_MARK_BLOCKS_FREE : 将以前申请的内存设为可用, 内存池的内存下次还能使用;

    MY_KEEP_PREALLOC: 保持以前新建的内存块

    0 释放内存)

    void free_root(MEM_ROOT *root, int flags);

    使用实例:

    free_root(&JsonMemRoot, 0); // 释放内存池内存

    free_root(&JsonMemRoot, MY_MARK_BLOCKS_FREE); // 标记已经使用的内存为释放, 下次可以继续使用, 不能使用宏MY_MARK_BLOCKS_FREE, 可以填入2

    3 mysql内存池如何工作的

    初始化定义每次分配的最小内存块大小M

    如果申请一次内存, 大小为X, X大于M, 就分配一块X的内存, 加入到管理链表中.

    如果小于的话, 看之前剩余的还够不够, 如果足够的话, 返回之前多余的内存地址.

    如果不够, 则申请这么大的内存, 也计入链表中

    释放是一次性的. 也可以不释放内存. 而是标记已经使用的内存为"未使用". 下次同样的应用可以继续使用

    4 两个最常用的使用场景

    一个很大的结构体, 很多操作步骤, 或者一个有很多连续步骤的函数. 大部分步骤都需要申请和释放内存.

    4.1 每个结构体或函数需要维护不同的内存

    如果是结构体, 则在结构体添加一个MEM_ROOT, 初始化结构体时也初始化MEM_ROOT; 如果是函数, 则在函数开始定义和初始化一个MEM_ROOT的局部变量. 中途任何位置出错或者完成 只需释放该MEM_ROOT即可.

    4.2 每个结构体或函数每次执行后的结果与中间申请的内存无关

    只需定义一个全局static MEM_ROOT变量, 程序启动时初始化MEM_ROOT; 程序退出时释放内存; 到了新的任务, 标记原来的内存为释放, 然后继续使用; 中途失败无需释放.

    5 mysql内存池的代码说明

    my_alloc.c 文件取自 mysql源码目录下的 mysys/my_alloc.c, 经过修改的得到

    my_alloc.h 文件取自 mysql源码目录下的 include/my_alloc.h, 经过修改的得到

    如果使用mysql环境, 可以不用加入这两个文件到工程, 而是按下面的方式引用文件, 之后直接使用上面的函数:

    #include

    #include

    #include

    #include

    6 一个简单的例子

    static void Test

    {

    MEM_ROOT JsonMemRoot;

    char * str;

    init_alloc_root(&JsonMemRoot, 1<<12, 0);

    str = (char *) alloc_root(&JsonMemRoot, 1024 * sizeof(char));

    if (NULL == str)

    {

    WriteLogNF("TestMemRoot : cannot alloc_root for str\\n");

    return -1;

    }

    strcpy(str, "hello\\n");

    puts(str);

    free_root(&JsonMemRoot, MY_MARK_BLOCKS_FREE);

    puts(str);

    free_root(&JsonMemRoot, 0);

    }

    展开全文
  • Boost内存池使用与测试 什么是内存池 内存池应用场景 安装 内存池特征 无内存泄露 申请内存数组没有被填充 任何数组内存块位置都和使用operator new[]分配内存块位置一致 ...

    QQ:386859647
    微信:herelsp
    转自: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。当然它还会申请一些空间用以管理链表,为方便述说,这里忽略这些内存。
      1. 用户不停的申请大小为memSize的内存,终于超过了内存池的大小,于是内存池启动重分配机制;
      1. 重分配机制会再申请一块大小为原内存池大小两倍的内存(那么第一次会申请64*memSize),然后将这块内存加到内存池的管理链表末尾;
      1. 用户继续申请内存,终于又一次超过了内存池的大小,于是又一次启动重分配机制,直至重分配时无法申请到新的内存块。
      1. 由于每次都是两倍于原内存,因此当内存池大小达到了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的内存。

    内存池溢出的终极方案

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

    转载于:https://www.cnblogs.com/herelsp/p/9410470.html

    展开全文
  • 一、为什么要使用内存池? 随着JVM虚拟机和JIT即时编译技术发展,对象...性能测试表明,采用内存池的ByteBuf相比于朝生夕灭ByteBuf,性能高23倍左右(性能数据与使用场景强相关)。 二、如何启动并初始化内存池

    一、为什么要使用内存池?

    随着JVM虚拟机和JIT即时编译技术的发展,对象的分配和回收是个非常轻量级的工作。但是对于缓冲区Buffer,情况却稍有不同,特别是对于堆外直接内存的分配和回收,是一件耗时的操作。而且这些实例随着消息的处理朝生夕灭,这就会给服务器带来沉重的GC压力,同时消耗大量的内存。为了尽量重用缓冲区,Netty提供了基于内存池的缓冲区重用机制。性能测试表明,采用内存池的ByteBuf相比于朝生夕灭的ByteBuf,性能高23倍左右(性能数据与使用场景强相关)。

    二、如何启动并初始化内存池?

    在Netty4或Netty5中实现了一个新的ByteBuf内存池,它是一个纯Java版本的
    jemalloc (Facebook也在用)。现在,Netty不会再因为用零填充缓冲区而浪费内存带宽了。 不过,由于它不依赖于GC,开发人员需要小心内存泄漏。如果忘记在处理程序中释放缓冲区,那么内存使用率会无限地增长。
    Netty默认不使用内存池,需要在创建客户端或者服务端的时候在引导辅助类中进行配置:

    // option是boss线程配置,childOption是work线程配置.
    				EventLoopGroup bossGroup = new NioEventLoopGroup();
    				EventLoopGroup workerGroup = new NioEventLoopGroup();
    					server.group(bossGroup, workerGroup)
    					      .channel(NioServerSocketChannel.class)
    					      .option(ChannelOption.SO_BACKLOG, 512)
    					      .childOption(ChannelOption.TCP_NODELAY, true)
    					      //Boss线程内存池配置.
    					      .option(ChannelOption.ALLOCATOR,PooledByteBufAllocator.DEFAULT)
                                                  //Work线程内存池配置.
    					      .childOption(ChannelOption.ALLOCATOR,PooledByteBufAllocator.DEFAULT);
    			        ChannelFuture f = server.bind(port).sync();

    三、如何在自己的业务代码中使用内存池?

                         首先,介绍一下Netty的ByteBuf缓冲区的种类:ByteBuf支持堆缓冲区和堆外直接缓冲区,根据经验来说,底层IO处理线程的缓冲区使用堆外直接缓冲区,减少一次IO复制。业务消息的编解码使用堆缓冲区,分配效率更高,而且不涉及到内核缓冲区的复制问题。

     

    ByteBuf的堆缓冲区又分为内存池缓冲区PooledByteBuf和普通内存缓冲区UnpooledHeapByteBuf。PooledByteBuf采用二叉树来实现一个内存池,集中管理内存的分配和释放,不用每次使用都新建一个缓冲区对象。UnpooledHeapByteBuf每次都会新建一个缓冲区对象。在高并发的情况下推荐使用PooledByteBuf,可以节约内存的分配。在性能能够保证的情况下,可以使用UnpooledHeapByteBuf,实现比较简单。

    在此说明这是当我们在业务代码中要使用池化的ByteBuf时的方法:

    第一种情况:若我们的业务代码只是为了将数据写入ByteBuf中并发送出去,那么我们应该使用堆外直接缓冲区DirectBuffer.使用方式如下:

    public class TestEncode extends MessageToByteEncoder<BasePropertyEntity> {
    	@Override
    	protected void encode(ChannelHandlerContext ctx, BasePropertyEntity msg,
    			ByteBuf outBuffer) throws Exception {
    		int length = 10;
    		//在此使用堆外缓冲区是为了将数据更快速的写入内核中,如果使用堆缓冲区会多一次堆内存向内核进行内存拷贝,这样会降低性能。
    		ByteBuf buffer = PooledByteBufAllocator.DEFAULT.directBuffer(length);
    		try {
    			byte[] context = new byte[length];
    			buffer.writeBytes(context);
    			
    			outBuffer.writeBytes(buffer);
    		} finally {
    			// 必须释放自己申请的内存池缓冲区,否则会内存泄露。
    			//outBuffer是Netty自身Socket发送的ByteBuf系统会自动释放,用户不需要做二次释放。
    			buffer.release();
    		}
    	}
    }

    第二种情况:若我们的业务代码中需要访问ByteBuf中的数组时,那么我们应该使用堆缓冲区HeapBuffer.使用方式如下:

    byte[] context = new byte[10];
    		int length = 10;
    		// 在此使用堆缓冲区是为了更快速的访问缓冲区内的数组,如果使用堆外缓冲区会多一次内核向堆内存的内存拷贝,这样会降低性能。
    		ByteBuf buffer = PooledByteBufAllocator.DEFAULT.heapBuffer(length);
    		try {
    			buffer.writeBytes(context);
    			// 高效率访问堆缓冲区的方式,具体原因随后会讲。
    			byte[] dst = new byte[10];
    			buffer.readBytes(dst);
    		} finally {
    			// 使用完成后一定要记得释放到内存池中。
    			buffer.release();
    		}

     

    展开全文
  • 固定长度的内存池有很多应用场景,比如图像编解码,码率固定导致每帧图像大小是比较固定,因此可以使用固定长度的内存池来优化图像缓存申请和释放。 隐式链表是一个单向链表,使用每块空闲内存...

    所谓固定长度内存池是指每次从内存池申请的内存长度是不能变的,如果内存池初始化每块内存的大小是100字节,那么每次申请出来的内存长度就是100字节,不会多也不会少。

    固定长度的内存池有很多的应用场景,比如图像的编解码,码率的固定导致每帧图像的大小是比较固定的,因此可以使用固定长度的内存池来优化图像缓存的申请和释放。


    隐式链表是一个单向的链表,使用每块空闲内存块的前四个字节来保存下一块空闲内存块的地址


    完整代码如下:

    class CFixSizeMemPool
    {
    public:
    
        //构造函数申请内存
        CFixSizeMemPool(unsigned int iBlockSize, unsigned int iBlockCount)
        {
            //由于需要用块内存构造空闲链表,因此块长度必须大于一个地址的长度
            if (iBlockSize < sizeof(void*))
            {
                iBlockSize = sizeof(void*);
            }
            m_iBlockSize = iBlockSize;
            m_iBlockCount = iBlockCount;
    
            //向操作系统申请整块内存
            m_pcMemPool = (char*)::malloc(iBlockSize * iBlockCount);
            if (m_pcMemPool == 0)
            {
                throw std::exception("no memory.");
            }
    
            //初始化空闲块链表
            buildFreeLink();
        }
    
        //析构函数释放整块内存
        ~CFixSizeMemPool()
        {
            ::free(m_pcMemPool);
            m_pcMemPool = 0;
        }
    
        //申请一块内存
        void* malloc(unsigned int iLen)
        {
            //申请的内存长度不能大于块的长度
            if ((iLen > m_iBlockSize) || (m_pcFreeBlock == 0))
            {
                return 0;
            }
    
            //分配出空闲块链表头上内存块,同时链表头指向下一空闲块
            char *pcBlock = m_pcFreeBlock;
            m_pcFreeBlock = *((char **)pcBlock);
            return pcBlock;
        }
    
        //释放一块内存
        void free(void *pMem)
        {
            //判断内存地址是否有效
            if ((pMem < m_pcMemPool) ||
                (pMem > (m_pcMemPool + (m_iBlockSize * m_iBlockCount))))
            {
                return;
            }
    
            //判断内存是否和块大小是对齐的
            if (((char*)pMem - m_pcMemPool) % m_iBlockSize != 0)
            {
                throw std::exception("invalid address.");
            }
    
            //把内存块放到空闲链表头上
            *((char **)pMem) = m_pcFreeBlock;
            m_pcFreeBlock = (char*)pMem;
        }
    
    private:
    
        //初始化空闲内存块队列
        void buildFreeLink()
        {
            char **pcNextFreePtr = 0;
            for (unsigned int i=0; i<m_iBlockCount; ++i)
            {
                pcNextFreePtr = (char**)(m_pcMemPool + (i * m_iBlockSize));
                *pcNextFreePtr = m_pcMemPool + (i+1) * m_iBlockSize;
            }
            *pcNextFreePtr = 0;
            m_pcFreeBlock = m_pcMemPool;
        }
    
        unsigned int m_iBlockSize;     //每块的大小
        unsigned int m_iBlockCount;    //块的数量
        char *m_pcFreeBlock;  //指向空闲块链表的头
        char *m_pcMemPool;    //指向整块内存的地址
    };






    展开全文
  • nginx内存池

    2019-07-15 22:36:00
    内存池到设计初衷: 1、效率:提前申请个池...管理一批具有相同生命周期资源,使用时只管申请不进行释放,然后在生命周期结束时直接销毁内存池进行资源释放。 注意:内存池除了管理内存,还可以使用ngx_pool_cle...
  • 一个内存分配方法好坏,一是体现在分配速度上,其重要性在内存容量较大时会显得尤其明显,二是体现在对内存这种有限资源的使用率上。如果空闲内存被分割成了许多无法利用内存块(也就是俗称的内存碎片),...
  • Talloc内存池使用教程——(2)过继一个talloc context过继一个talloc contextTalloc拥有变更一个context父节点能力。这个操作通常被称为过继(译者注:原文为stealing,为更加易懂翻译为过继),它是最重要...
  • 本文分析一下可能引起java内存泄露的场景:通过 finalize() 方法终结器finalizers的使用是潜在内存泄漏问题的另一个来源。每当类的 finalize() 方法被重写时,该类的对象不会立即被垃圾回收。相反,GC将它们排队等待...
  •  采用内存池,需要内存时候,向内存池拿,使用完后就进行释放。     固定内存 内存池 优点 1 实现简单 2 直接使用,效率高 1 适合任何场景 缺点 1 受到应用场景限制 1 需要管理...
  • 内存池笔记

    2013-08-06 15:52:40
    在《提高c++性能编程技术》这本书中,第六章对于该场景给出了一个内存池例子,现将其代码整理如下并统计出同未优化仅仅使用new delete时候耗费时间对比。 其基本思路是,在频繁分配固定大小内存时候,先建立...
  • 1.引言 如果你开发过Nginx第三方模块,会发现虽然我们在写C语言代码,但是我们不需要关系内存释放. ...分配的内存池的大小,但是Nginx官方上通过会写着不要去改这样配置,究竟要不要改呢? ...
  • 内存泄漏的场景

    2021-04-10 19:40:51
    如果使用连接,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量Statement 对象无法释放,从而引起内存泄漏。...
  • nginx的使用场景 2. nginx源码 内存池,线程池,日志 3. nginx多进程网络实现 c/c++ linux服务器开发免费学习地址:c/c++ linux后台服务器高级架构师 在编程过程中,尤其是对于C语言开发者,其实编程就是在...
  • Java数据库连接比较及使用场景

    千次阅读 2016-05-21 13:48:38
    数据库连接是一种池化技术,预先创建好数据库连接,保存在内存中,当需要连接时,从中取出即可,使用完后放回连接。下面我们介绍Java中常用数据库连接,主要介绍内容有以下几点: 1. 优点及不足 2. ...
  • 应用程序自定义的内存池根据不同适用场景又有不同类型。 从线程安全角度来分,内存池可以分为单线程内存池和多线程内存池。单线程内存池整个生命周期只被一个线程使用,因而不需要考虑互斥访问...
  • 为什么需要内存池? 1.效率问题:如果我们直接向系统申请内存,当我们需要频繁申请释放内存时,就需要频繁与系统层产生交互,多次切换用户态和内核态,而用户态和内核态之间切换消耗是非常大,因此申请...
  • 一个内存分配方法好坏,一是体现在分配速度上,其重要性在内存容量较大时会显得尤其明显,二是体现在对内存这种有限资源的使用率上。如果空闲内存被分割成了许多无法利用内存块(也就是俗称的内存碎片),那么...
  • 1. nginx的使用场景 2. nginx源码 内存池,线程池,日志 3. nginx多进程网络实现 视频讲解如下,点击观看: 【Linux后台开发系统】nginx源码分析之内存池与线程池丨nginx多进程网络实现 C/C++Linux服务器...
  • 一个内存池C++类实现

    千次阅读 2011-04-26 11:17:00
    <br />一个内存池C++类实现 在程序设计领域,程序员经常需要及时动态地产生出一些小型对象,例如读取解析文件时临时需要缓冲区,动态创建视图时需要视图对象,游戏程序中怪物,特效,场景物乃至于...
  • 最近经常有人问我在Java中使用堆外(off heap)内存的好处与用途何在。我想其他面临几样选择人应该也会对这个答案感兴趣吧。 堆外内存其实并无特别之处。线程栈,应用程序代码,NIO缓存用都是堆外内存。事实上...
  • Nginx的内存池的设计非常精巧,很多场景下,我们可以将Nginx的内存池实现抽象出来改造成我们开发中的内存池内存池 一般我们使用malloc/alloc/free等函数来分配和释放内存。但是直接使用这些函数会有一些弊端...
  • 最近经常有人问我在Java中使用堆外(off heap)内存的好处与用途何在。我想其他面临几样选择人应该也会对这个答案感兴趣吧。堆外内存其实并无特别之处。线程栈,应用程序代码,NIO缓存用都是堆外内存。事实上在C...
  • 通过剖析开源代码可以积累优秀代码设计思想和良好编程规范,了解不同应用场景下不同的内存池实现也是一种重要能力,本仓库对SGI STL二级空间配置器内核和nginx内存池内核进行了剖析,并使用C ++ OOP进行...
  • (Ps:全篇读完后再回头来看一下封面图) 0.前言 在我们写业务代码过程中,业务代码与系统内核...几乎所有程序都在使用 C 库内存池分配出内存,比如java使用ptmalloc,golang使用tcmalloc等等。C 库内存池影响着系.
  • Nginx的内存池的设计非常精巧,很多场景下,我们可以将Nginx的内存池实现抽象出来改造成我们开发中的内存池。 1 为何使用内存池 一般我们使用malloc/alloc/free等函数来分配和释放内存。但是直接使用这些函数会有...
  • 内存池简单实现(一)

    千次阅读 2018-08-08 15:21:23
    概念 内存池就是在程序启动时,预先向堆中申请一部分内存,...而数据包内存采用原始new-delete模式,会大大降低服务器性能,所以想到了使用内存池技术。 在此实例中有几个硬性条件: 1、数据包最大长度可...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 454
精华内容 181
关键字:

内存池使用的场景