1 AllocSet 的内存分配涉及到的方法和数据结构
话说
MemoryContextMethods 结构里的函数实现了pg 里AllocSet 和MemoryContext 的内存管理机制,定义见下面。
typedef struct MemoryContextMethods
{
void *(*alloc) (MemoryContext context, Size size);
/* call this free_p in case someone #define's free() */
void (*free_p) (MemoryContext context, void *pointer);
void *(*realloc) (MemoryContext context, void *pointer, Size size);
void (*init) (MemoryContext context);
void (*reset) (MemoryContext context);
void (*delete ) (MemoryContext context);
Size (*get_chunk_space) (MemoryContext context, void *pointer);
bool (*is_empty) (MemoryContext context);
void (*stats) (MemoryContext context);
#ifdef MEMORY_CONTEXT_CHECKING
void (*check) (MemoryContext context);
#endif
} MemoryContextMethods;
其中alloc 由静态函数AllocSetAlloc() 实现,具体签名在下面。它实现了AllocSet 相关的内存分配机制。
static void * AllocSetAlloc(MemoryContext context, Size size)
在写AllocSet 内存分配机制之前,先看两个和内存分配与管理密切相关的结构 Allockblock 和AllockChunk ,这个在 前面的文章http://beigang.iteye.com/blog/1266554和http://beigang.iteye.com/blog/1279177里已经 提到不 止一次了。
Allockblock 和AllockChunk 都是内存里的块,AllockBlock 类型是AllockBlockData * , AllocChunk 类型是AllockChunkData* ,AllocBlock 是AllocSet 用malloc 分配的内存单元,包含着一到多个AllockChunk ,AllocChunk是 MemoryContextMethods.alloc 请求分配的内存单元,将来由MemoryContextMethods . free_p 释放,放到AllocSet 的freelist 链表数组的大小合适的链表里,以后被MemoryContextMethods.alloc 再分配。
AllockBlockData 和AllockChunkData 的定义如下
typedef struct AllocBlockData
{
AllocSet aset; /* aset that owns this block */
AllocBlock next; /* next block in aset's blocks list */
char *freeptr; /* start of free space in this block */
char *endptr; /* end of space in this block */
} AllocBlockData;
typedef struct AllocChunkData
{
/* aset is the owning aset if allocated, or the freelist link if free */
void *aset;
/* size is always the size of the usable space in the chunk */
Size size;
#ifdef MEMORY_CONTEXT_CHECKING
/* when debugging memory usage, also store actual requested size */
/* this is zero in a free chunk */
Size requested_size;
#endif
} AllocChunkData;
2 AllocSetAlloc
下面就写 MemoryContextMethods.alloc 的实现者AllocSetAlloc() 这个函数。这个内存分配过程还是有点复杂的,下面先上个图,然后分块解读处理流程。
AllocSetAlloc 分配内存流程图
根据上面的图,按红紫蓝黄绿的顺序做简要分解,具体的还是看流程图吧,算法基本上都在图里面了。
先声明一点,TopMemoryContext 所用内存是用malloc 独立分的,其他的AllocSet 都是调用AllocSetAlloc 分得。
现在分解红色块。首先传进来要在其上分配内存的AllocSet 和请求的大小,如果请求大小没有超过AllocChunk 的最大大小8k ,就在该AllocSet 的空闲可用AllocChunk 链表数组freelist (后面分析这个freelist )合适大小的链表里找有没有可以使用的AllocChunk ,如果没有的话就把请求大小加上AllocChunk 的大小(图中的Q ),然后在AllocSet 的AllocBlock 链表blocks 里找,看blocks 有没有满足大小的可用block ,如果没有,就malloc 分配一块block 。红色块到此结束。
接下来是紫色块,在上面分配的block 里分一个AllocChunk ,然后把该chunk 作为MemoryContext 类型返回,至此就分得了一块内存。
蓝色框里是如果请求的大小超过了AllocChunk 的最大值8k ,就malloc 分配一个请求大小的block ,将其加入AllockSet 的blocks 链表中。在这个block 里面分配一个chunk ,作为MemoryContext 类型返回。
黄色框里是如果前面在AllocSet 的freelist 链表数组中的合适的链表里找到了可用chunk ,就把该chunk 从链表中取出来,作为MemoryContext 类型返回。
现在就剩绿色框了。绿色框是维护前面多次提到的freelist 链表数组的,现在是分析这个空闲AllocChunk 链表数组的时候了。先看这个freelist 。AllocSet 可分配的AllocChunk 的大小是从2 的3 次方依次到2 的13 次方字节,也就是从8 字节依次到8k 字节。这样做的好处是可以方便管理内存,要是按实际需求分配的话对于内存再分配是个很大的挑战,会造成内存浪费。结构图见下面。
AllocSet 的freelist 结构图
绿色框里就是说如果在AllocSet 的AllocBlock 链表blocks 里找到了block ,但是该block 的可用空闲空间不够请求大小时就把空闲大小按2 的13 次方到2 的3 次方依次分成AllocChunk 加入到对应大小的freelist 链表数组中的链表里。到此绿色块完了,这个AllocSetAlloc 也结束了。
3 示例
下面再用图例演示从TopMemoryContext 的AllocBlock 链表blocks 里一个block 分配一个AllockChunk 类型的变量chunk ,然后在chunk 上初始化ErrorContext 的例子。下图是只有一个AllocBlock 的TomMemoryContext 结构图
从TopMemoryContext 的AllocBlock 链表blocks 里的block 中分配了一个AllockChunk 变量chunk ,就是图纸粉色的那一块。
把ErrorContext 放在了新分的chunk 上。