在innodb中实现了自己的内存池系统和内存堆分配系统,在innodb的内存管理系统中,大致分为三个部分:基础的内存块分配管理、内存伙伴分配器和内存堆分配器。innodb定义和实现内存池的主要目的是提供内存的使用率和效率,防止内存碎片和内存分配跟踪和调试。我们先来看看他们的关系和结构。

下面是它的关系结构图:

上图中的:

ut_mem_block块是基础内存管理

Buddy allocator是内存伙伴分配器

mem_heap是内存堆分配器

1.基础内存管理

innodb中的内存分配和内存释放是通过统一的结构进行管理,详细的实如今ut0mem.h和ut0mem.c其中。其中最重要的就是对malloc和free的封装。

通过一个链表结构体来管理已经分配的内存。结构体例如以下:

 typedef ut_mem_block_struct
{
ulint size; /*这个被分配block的内存大小*/
ulint magic_n; /*节点魔法字,用于校验所用*/
UT_LIST_NODE_T(ut_mem_block_t) mem_block_list; /*block list node,指定prev node和next node*/
};

关于block的list定义是个全局的变量。UT_LIST_BASE_NODE_T(ut_mem_block_t)
ut_mem_block_list;全部分配的block都会增加到这个list其中。

在ut_malloc_low函数分配内存的时候会将分配的block增加到list其中。在ut_free的时候会所释放的内存所在的block从list其中删除。

除了这两个函数以外,innodb还提供ut_free_all_mem函数来释放全部分配的block和统计分配内存的总数ut_total_allocated_memory功能。

 基础内存管理的方法例如以下:

        ut_malloc_low                    分配一个n长度的内存块,并将分配的块记录到ut_mem_block_list其中.

        ut_malloc                            与ut_malloc_low功能同样,可是会用0初始化所分配的内存。

        ut_free                                释放一个分配的内存块,并将其从ut_mem_block_list其中删除。

ut_free_all_mem                 释放ut_mem_block_list全部的内存块并清空ut_mem_block_list

以上函数是支持多线程并发操作的。也就是说是线程安全的。

 innodb这样做的目的是保证全部malloc出去的内存都在 ut_mem_block_list其中,以便管理。
    基础内存管理的结构例如以下:

2.伙伴分配器

 innodb的伙伴分配器是基于ut_malloc_low函数之上的内存管理器,在创建伙伴分配器时,innodb会一下用ut_malloc_low开辟一个非常大的内存块,然后用伙伴分配来分配这个块的内存使用。

innodb的伙伴分配器是基于2的基数为基础的管理方式,其buddy alloc pool的定义例如以下:

   struct mem_pool_struct
{
byte* buf; /*总体内存的句柄*/
ulint size; /*总体内存大小*/
ulint reserved; /*当前分配出去的总内存大小*/
mutex mutex; /*多线程相互排斥量*/
UT_LIST_BASE_NODE_T(mem_area_t) free_list[64]; /*area_t链表数组,每一个数组单元能管理2的i次方内存块列表,i是数组的下标*/
};
   struct mem_area_struct
{
ulint size_and_free; /*area的内存大小(一定是2的次方),最后一个bit表示是否已经释放*/
UT_LIST_NODE_T(mem_area_t) free_list; /*area链表的上下area,由于buddy area是会分裂的,有可能多个*/
};

mem_area_t是一个buddy的内存区域。也就是mem_area_struct。

下面是一个32位机器管理1024字节内存块的buddy list分布:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnJ4ZHU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

每个area是有mem_area_t头和可分配的内存(memory_buffer)确定的,memory_buffer的长度不小于mem_area_t头的长度,在32位机器上mem_area_t的头应该是16个字节(8字节对齐)。


2.1mem_area_t的分裂

  在内存分配的过程中,有可能会造成mem_area_t的分裂。还是以上面的样例来说。增加我们要分配一个200字节的内存,这时候伙伴分配器的分配流程是这种:
        1.找到一个离200+sizeof(mem_area_t)近期的2的i次方的数(256),确定i = 8。
        2.在free_list[i]的列表中查找是否有空暇的node,假设有,将node职位no free.假设没有。对i + 1层运行查找是否有可用的内存。
        3.在上面的样例中,i+1=9,free_list是空的,继续在i+2层找,一次类推。直到找到有node的层,也就是i = 10;
        4.首先对10层进行分裂。分裂成两512大小的第9层节点,并从10删除area。在第9层增加2个512的node.
        5.然后在对第9层的第一个节点进行分裂,分裂两个大小为256的第8层节点,并从第九层删除,在第8层增加2个节点。
        6.将第一个256大小的area分配给相应的操作者,并置为no free标识。

    下面是分配了一个200字节的内存池结构:

假设分配出去后的area_t会从free_list[i]链表中删除,也就是说在buddy上将是记录的。

2.2mem_area_t的合并

假设200字节分配出去后,使用完成会归还给buddy allocator,还是拿上面的样例来说。就会发生area合并的情况,过程例如以下:

    1.使用者归还伙伴分配的内存,首先会依据area_t的信息去找到自己的buddy,也就是第8层另外一个没有被分配的area.

    2.找到buddy area后。推断buddy area是否是释放状态,假设是,触发合并。将自己和buddy area从第8层删除,合并成一个512大小的第9层area,

    3.在反复1 ~ 2步,又会将自己和第九层另外一个buddy area合并成一个1024大小的第10层area.

2.3buddy allocator的接口函数:

    mem_pool_create                构建一个buddy allocator

    mem_area_alloc                   用buddy allocator分配一块内存

    mem_area_free                    将一块内存归还给buddy allocator

    mem_pool_get_reserved      获得buddy allocator已经使用的内存大小


3内存分配堆(memory heap)

innodb中的内存管理终于的体现形式是mem_heap_t内存分配与管理,全部关于内存分配的操作都会调用mem_heap的API方法,mem_heap_t的结构定义例如以下:
struct mem_block_info_struct
{
ulint magic_n; /*魔法字*/
char file_name[8]; /*分配内存的文件*/
ulint line; /*分配内存的文件所在行*/
ulint len; /*block的长度*/
ulint type; /*依赖的底层分配类型,有DYNAMIC、BUFFER、BTR_SEARCH三种类型*/
ibool init_block; /*是否是外部分配的内存块*/ ulint free; /*被占用的空间大小*/
ulint start; /*可分配内存的起始位置*/
byte* free_block; /*备用block,只在BTR_SEARCH方式可用*/ UT_LIST_BASE_NODE_T(mem_block_t) base;
UT_LIST_NODE_T(mem_block_t) list;
};

备注:mem_block_info_struct/mem_block_info_t/mem_block_t/mem_heap_t是等价

mem_heap_t的内存结构例如以下:



关于mem_heap_t的几个要点:

    1.一个mem_block_t最小空间不小于64字节,标准的大小是8KB,在非MEM_HEAP_BUFFER模式下分配的空间不大于page size - 200(page size一般为16KB)



    2.mem_heap_t有三种类型,各自是DYNAMIC、BUFFER、BTR_SEARCH。在DYNAMIC模式下都是基于buddy allocator进行mem_block_t分配的。在BTR_SEARCH模式下,使用free_block来作为内存分配,在BUFFER模式下比較复杂。假设分配的内存大小< page size的一半时,使用buddy alloc,否则使用buf_frame的内存分配方式(这个是属于buf0buf.XX里面的方式。还未開始分析)。



    3.mem_heap_t在分配新的mem_block_t的时候一定是分配一个heap最后节点大小的两倍,假设分配的大小超过MEM_MAX_ALLOC_IN_BUF(相当于一个page size)的时候,heap 类型推断,在不是DYNAMIC模式下。最大就是一个MEM_MAX_ALLOC_IN_BUF大小。假设其它模式下就是设置成MEM_BLOCK_STANDARD_SIZE标准大小。在这些限制外,假设须要分配的内存大于这些限制,以分配内存大小为准进行mem_block_t分配。分配好的mem_block_t总是增加到heap
base list的最后,也就是heap堆栈的顶端。

4.mem_heap_t在释放mem_block_t时候总是从顶端開始释放,直到不能释放为止(mem_block_t没有被使用者归还)。在mem_block_t释放的时候也是须要參考DYNAMIC、BUFFER、BTR_SEARCH类型进行相对于的归还规则(和2要点是相相应的)。


mem_heap_t函数方法说明:

mem_heap_create                                        用DYNAMIC模式创建一个mem_heap_t

mem_heap_create_in_buffer                        用BUFFER模式创建一个mem_heap_t

mem_heap_create_in_btr_search                 用BTR_SEARCH模式创建一个mem_heap_t

mem_heap_free                                            释放mem_heap_t对象

mem_alloc                                                    创建在MEM_HEAP_DYNAMIC模式下。并分配一块指定大小的内存(在这样的方式下mem_heap_t仅仅会有一个mem_block_t)

mem_free                                                      归还mem_heap_t分配的内存,并释放mem_heap_t

mem_heap_alloc                                           在指定的mem_heap_t上分配一块内存

mem_heap_get_heap_top                            获得heap顶端块可使用内存的地址

mem_heap_empty                                        清空指定的mem_heap_t

mem_heap_get_top                                     获得heap顶部的指定n大小的mem_block_t指针

mem_heap_free_top                                    释放heap顶部N大小的mem_block_t块

4总结

innodb提供内存池和heap分配方式来统一管理内存,最基本的目的是提高内存的率。

在MySQL-5.6的版本号中。innodb提供两种选择,一种是使用innodb提供的内存池管理内存,另一种是提供系统的malloc和free来作为内存管理。MySQL默认的是系统管理内存方式,一些有经验的DBA会使用系统的管理内存方式+TMalloc来做内存优化。借助TMalloc高效的内存管理方式实现MySQL的性能提升。






最新文章

  1. LDAP与禅道
  2. HDU 2824 简单欧拉函数
  3. 常见JAVA框架
  4. Kendo UI for ASP.NET MVC 的一些使用经验(转)
  5. 【jmter】JDBC进行mysql数据库测试
  6. js冒泡事件之之之
  7. POJ 1679 The Unique MST(次小生成树)
  8. Integral类型的跨平台使用
  9. DB2因表空间不够产生load表失败
  10. eclipse 最有用的10个快捷键
  11. 移植最新u-boot(裁剪和修改默认参数)之韦东山笔记
  12. Myeclipse中可以正常显示,但运行后的网页找不到图片
  13. java 基本类型和包装类的比较
  14. Eclipse 运行ant build.xml
  15. angular何时触发脏检查机制
  16. sublime 使用快捷键
  17. 使用datepicker和uploadify的冲突解决(IE双击才能打开附件上传对话框)
  18. 2017-2018-2 20165314实验二《Java面向对象程序设计》实验报告
  19. rsync 故障排查整理
  20. Json对象与Json字符串的转化、JSON字符串与Java对象的转换(转)

热门文章

  1. 使用github作为maven仓库
  2. base64记载
  3. 第1节 hive安装:2、3、4、5、(多看几遍)
  4. XP禁用了U盘和移动硬盘方法
  5. html页面加载初始化方法
  6. PHP 反射API
  7. sort 结构体 正数负数分开排序
  8. UVa 1600 Patrol Robot(BFS)
  9. SpringBoot第十六篇:自定义starter
  10. c网购物车流程图