SQLite3源码学习(12)动态内存分配分析

SQLite自身实现了一个动态内存分配的子系统,源代码在malloc.c里面,在这个系统里,包含一个可选的底层内存分配器,默认的实现在mem1.c里面,源码里还提供了其他多种实现,甚至应用程序自己也可以实现一个来替换掉默认的内存分配器。

对于SQLite的动态内存分配,官方的一篇详细的文档来介绍:

http://blog.csdn.net/zhoudaxia/article/details/8257784

这里主要再结合源码分析一下。

1.通用接口

         通用接口既可以给内核用,也可有由应用程序调用,主要是由以下几个函数组成:

void *sqlite3_malloc(int n);
void *sqlite3_realloc(void *pOld, int n);
void sqlite3_free(void *p);
sqlite3_uint64 sqlite3_msize(void *p);
void *sqlite3MallocZero(u64 n);

看名字就可以知道,前面3个是对库函数mallocreallocfree的封装,而sqlite3_msize用来查看分配的内存大小。

封装主要做了以下几件事:

1)统计内存的使用情况,由以下函数完成:

sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); //记录分配的最大值
nFull = sqlite3MallocSize(p);
sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull);//记录内存分配的总量
sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); //记录分配的次数

2)内存分配请求失败或超过限制时,释放空闲的缓存页并重新分配:

sqlite3MallocAlarm(nFull);// nFull是请求分配的内存大小,按8字节对齐。

3)为了保证线程安全,分配和释放时要加互斥锁:

sqlite3_mutex_enter(mem0.mutex);
……
sqlite3_mutex_leave(mem0.mutex);

2内部接口

内部接口主要针对sqlite本身使用,使用时需要传入特定的数据库连接,接口由以下函数组成:

void *sqlite3DbMallocRaw(sqlite3 *db, u64 n);’
void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n);
void sqlite3DbFree(sqlite3 *db, void *p);
void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, u64 n);
void *sqlite3DbMallocZero(sqlite3 *db, u64 n);
int sqlite3DbMallocSize(sqlite3 *db, void *p);

相对于通用接口,内部接口会先判断分配内存的大小,如果内存较小会使用lookaside来分配,如果分配的内存超过每个slot的大小,那么使用通用接口来分配,这个在上一篇已经说过。

对于同一个数据库连接,如果一次内存分配失败,那么以后都直接返回失败,直到db->mallocFailed0,或者释放本次连接。

3临时内存分配

SQLite偶尔需要一大块的“临时”内存来执行一些临时的计算。例如,当重新平衡一棵B-Tree时,需要使用临时内存。这些临时内存通常在10KB左右,用于一个单一的、短暂的函数调用。

如果内存较大,可以直接从栈中获取。对于小容量栈的处理器,如大部分嵌入式系统中,在栈中申请一个大的缓存区会出现问题,这就需要在堆中申请。

临时内存分配器的设置方法如下:

sqlite3_config(SQLITE_CONFIG_SCRATCH, pBuf, sz, N);

其中pBuf指向一段连续的内存,SQLite用它来进行临时内存分配。这段连续内存至少要有sz*N字节的大小,"sz"参数是每次临时内存分配的最大字节数,N是同时进行临时内存分配的最大次数。"sz"参数值应该为最大数据库页面的6倍左右,N应该为系统中运行线程数量的2倍左右。

使用时类似lookaside,首先初始化,把pBuf分割成Nsz大小的slot然后用链表连接起来,让链表的第一个slot作为mem0.pScratchFree  

    int i, n, sz;
    ScratchFreeslot *pSlot;
    sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch);
    sqlite3GlobalConfig.szScratch = sz;
    pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch;
    n = sqlite3GlobalConfig.nScratch;
    mem0.pScratchFree = pSlot;
    mem0.nScratchFree = n;
    for(i=0; i<n-1; i++){
      pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot);
      pSlot = pSlot->pNext;
    }
    pSlot->pNext = 0;
    mem0.pScratchEnd = (void*)&pSlot[1];

分配时摘下链表的第1slotmem0.pScratchFree,然后让mem0.pScratchFree指向下slot

    p = mem0.pScratchFree;
    mem0.pScratchFree = mem0.pScratchFree->pNext;
    mem0.nScratchFree--;

释放的slot指向原来第一个slot,然后更新mem0.pScratchFree为当前释放的slot  

   pSlot->pNext = mem0.pScratchFree;
      mem0.pScratchFree = pSlot;
      mem0.nScratchFree++;

4初始化

动态内存分配的子系统初始化主要再sqlite3MallocInit()里完成,做了以下几件事:

1)设置默认底层内存分配器

sqlite3MemSetDefault();

2)初始化临时内存分配器Scratch

3)初始化底层内存分配器

sqlite3GlobalConfig.m.xInit()

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值