Chinaunix首页 | 论坛 | 博客
  • 博客访问: 150609
  • 博文数量: 12
  • 博客积分: 226
  • 博客等级: 二等列兵
  • 技术积分: 221
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-10 23:15
文章分类

全部博文(12)

分类: Mysql/postgreSQL

2012-11-24 11:05:03

c/c++容易出现内存泄露问题,这是因为它们需要直接操作内存,new/malloc分配内存,delete/free释放,需要一一对应,很多时候分配和释放内存之间有着复杂的过程,或者出错之前没有释放内存就出现内存泄露了,而这对于需要保证高可用性的数据库而言是不可接受的

MySQL通过MEM_ROOT可以保证分配的内存都被释放了,基本原理是:记录所有向操作系统'借'的所有内存块,通过alloc_root/multi_alloc_root函数向调用者返回相应大小内存,在释放时将其统一归还给操作系统,代价是:实际分配了更多的内存;额外的内存管理

代码位置:include/my_alloc.h,mysys/my_alloc.c

两个重要的数据结构:MEM_ROOT  USED_MEM 

先来解释MEM_ROOT,这个结构体用于管理向操作系统'借'的所有内存块,free链表将有剩余空间的内存块链接起来,used链表记录所有空间使用得差不多(每次分配内存都会通过left_size或者first_block_usage判断是否将free中第一个block从free移到used中)的内存块,pre_alloc为预分配的内存空间,min_malloc会被初始化为32,first_block_usage这个参数比较有意思,每次从free链表中的第一个block分配内存失败(剩余空间不满足)后会增加1,而当在某次分配length大小内存时,如果发现free链表中的第一个内存块中没有足够的内存且剩余空间不够4096,而这时first_block_usage大于10的话,会将block从free链表移到used链表中,并且将first_block_usage重新置为0,error_handler是一个函数指针,看函数名就知道其作用了

点击(此处)折叠或打开

  1. typedef struct st_mem_root
  2. {
  3.   USED_MEM *free; /* blocks with free memory in it */
  4.   USED_MEM *used; /* blocks almost without free memory */
  5.   USED_MEM *pre_alloc; /* preallocated block */
  6.   /* if block have less memory it will be put in 'used' list */
  7.   size_t min_malloc;
  8.   size_t block_size; /* initial block size */
  9.   unsigned int block_num; /* allocated blocks counter */
  10.   /*
  11.      first free block in queue test counter (if it exceed
  12.      MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list)
  13.   */
  14.   unsigned int first_block_usage;

  15.   void (*error_handler)(void);
  16. } MEM_ROOT;

继续来看USED_MEM这个结构体,它代表一个内存块,其中有内存块的总大小size,剩余大小left,还有一个指向下一个内存块的指针next,但是直到现在我们都还没有看到真正的内存在哪,MEM_ROOT如何向外分配出内存?

点击(此处)折叠或打开

  1. typedef struct st_used_mem
  2. {
  3.   struct st_used_mem *next; /* Next block in use */
  4.   unsigned int left; /* memory left in block */
  5.   unsigned int size; /* size of block */
  6. } USED_MEM;

其实,USED_MEM这个结构体中能够看到的成员实际上一个内存块的元数据,真正的的内存是紧接着该结构体之后,内存大小为size-ALIGN_SIZE(sizeof(USED_MEM)),通过left记录剩余空闲内存大小,可以看出内存块之间通过next指针形成了一个链表

到这里,再看MEM_ROOT中的一下函数就很容易了:

init_alloc_root
初始化MEM_ROOT,如果pre_alloc_size不为0,就预分配一个大小为pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)的内存块,并将pre_alloc和free指针指向它,看出来了,真正的内存从结构体地址之后的ALIGN_SIZE(sizeof(USED_MEM)开始

alloc_root
向外分配内存的函数,从free链表中找到一个大小满足的内存块,对于第一个block,会执行first_block_usage++; 如果其中没有足够的内存且剩余空间不够4096,而这时first_block_usage大于10的话,会将该block从free链表移到used链表中,继续搜索链表中的block,如果能找到一个大小满足的块,就从其中分配,如果没能找到,就向操作系统'借'一块内存,大小为block_size * (block_num >> 2)和length+ALIGN_SIZE(sizeof(USED_MEM))中的最大值,将其放到free链表最后,分配完内存后,block中left会减小分配出去的大小,如果left

multi_alloc_root
弄明白了alloc_root,这个就很容易了,这里不做过多解释

free_root
标记释放:MY_MARK_BLOCKS_FREE,将free和used链表中所有block中的left重置(size-ALIGN_SIZE(sizeof(USED_MEM)),used链到free链表末尾,以及重置MEM_ROOT中的used和first_block_usage
真正释放:遍历free和used链表,向操作系统归还所有'借'来的内存块

阅读(3844) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~