话说MemoryContextMethods结构里的函数实现了pg里AllocSet/MemoryContext的内存管理机制,定义见下面。
typedef structMemoryContextMethods
{
void *(*alloc) (MemoryContext context, Sizesize);
/* 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;
其中realloc由静态函数AllocSetRealloc()实现,具体签名在下面。它实现了AllocSet/MemoryContext相关的内存再分配。AllocSetRealloc()方法返回按请求大小新分配的内存的指针。把新分配的内存加入到set中,把传进来的pointer相关的旧内存里的内容拷贝到新内存里,把旧内存释放掉。新分配的内存有可能是在原来的基础上再在后面加一部分,这样就不用拷贝旧内存内容和释放就内存了。
static void * AllocSetRealloc(MemoryContext context, void *pointer, Size size)
在某个context里的chunk的内存不够用的时候,就调用MemoryContext的repalloc()方法,该方法再调用AllocSetRealloc()方法调整原来分配给AllocChunk类型实例chunk的大小。
下面就写MemoryContextMethods. realloc的实现者AllocSetRealloc ()这个函数。先上图,然后分块解读处理流程。
AllocSetFree回收内存流程图
先看红色框,根据传进来了要扩展内存的chunk和其所在的context,检查该chunk原来分配的空间oldsize和本次请求的空间size,如果原oldsize可以满足要求,就把该chunk的请求大小request_size置成size,返回该chunk。如果oldsize不能满足本次请求的大小size。就根据chunk原来分配的空间oldsize的是否大于AllocChunk的大小上限/8k判断,大于该上限就到黄色框,负责到蓝色框。
接着看黄色框,根据该chunk找到其所在的block,以该block和根据请求的chunk大小size换算的AllocBlock的东西blksize调用realloc()方法分配内存,成功就返回对应chunk的指针,负责写日志报错“out of memory”
再接着看蓝色框,检查chunk是否是其所在block里的最后一个AllocChunk,如果是,检查该block剩余的空间和该chunk的空间加起来是否满足全球的大小size,如果可以就在该block中开展这个chunk,然后返回该chunk的指针。负责就到了紫色框。
最后看紫色框,调用AllocSetAlloc()方法(参见《pg的内存管理机制一》)在当前MemoryContext上分配空间新的空间该chunk,把chunk原来内存里的内容拷贝过来,调用AllocSetFree()释放chunk原来所占的内存。具体看流程图吧。