先来看一下 lua 的内存分配。
内存分配的文件是 lmem.h 及对就的 lmem.c。
内存分配方法为 luaM_realloc 和 luaM_growaux。
其它的内存操作接口是定义的宏,通过调用上面的两个基本方法实现。
看一下实现:
/*** generic allocation routine.*/void *luaM_realloc (lua_State *L, void *block, lint32 size) { if (size == 0) { free(block); /* block may be NULL; that is OK for free */ return NULL; } else if (size >= MAX_SIZET) lua_error(L, "memory allocation error: block too big"); block = realloc(block, size); if (block == NULL) { if (L) luaD_breakrun(L, LUA_ERRMEM); /* break run without error message */ else return NULL; /* error before creating state! */ } return block;}
核心是调用 realloc 那一句来分配内存。
其它的是一些边界处理。
size 为 0 时则释放 block 内存。(realloc 第二个参数为 0 时也可以释放内存。)
一次最大不可申请超过 MAX_SIZET 的内存。
void *luaM_growaux (lua_State *L, void *block, size_t nelems, int inc, size_t size, const char *errormsg, size_t limit) { size_t newn = nelems+inc; if (nelems >= limit-inc) lua_error(L, errormsg); if ((newn ^ nelems) <= nelems || /* still the same power-of-2 limit? */ (nelems > 0 && newn < MINPOWER2)) /* or block already is MINPOWER2? */ return block; /* do not need to reallocate */ else /* it crossed a power-of-2 boundary; grow to next power */ return luaM_realloc(L, block, luaO_power2(newn)*size);}
用来为数组动态增长时分配内存。
申请内存时以 2 的整数次幂边界对齐。因为有这个限制,在某些情况是不需要分配内存的。比如,nelems 为 10, inc 为 5, 就不需要分配内存,因为原来的为 10 分配内存的时候已经分配为 16 了(大于 10 的最小的 2 的整数次幂的数。)。翻译成业务语言就是,如果数组的元素个数由 10 个增长到 15 个,程序并不会进行内存分配。因为元素为 10 个的数组的内存分配量实际上是 16 个元素。
luaO_power2 的实现如下所示:
/*** returns smaller power of 2 larger than `n' (minimum is MINPOWER2)*/lint32 luaO_power2 (lint32 n) { lint32 p = MINPOWER2; while (p<=n) p<<=1; return p;}
最后也是调用 luaM_realloc 来完成内存的分配。
顺便看一下那几个用来 DEBUG 的内存分配接口。
调试用的内存分配时,每个分配的内存块后面加了一个标记用的块。
如果程序运行中发现标记块内容不对了,一定是有程序写越界了。
方便程序员查找出问题所在。
在其它程序里,未分配的内存可能被设置为其它的值。
比如 VC 里著名的”烫烫烫“,或者 “屯屯屯”。
说到这儿,怎么少得了下面的这个小对联:
手持两把锟斤拷,口中直呼烫烫烫。
----------------------------------------
到目前为止的问题:
> 函数原型优化 luaU_optchunk
> 打印函数原型 luaU_printchunk
> dump 函数原型 luaU_dumpchunk
> ZIO 是什么
> 语法分析 luaY_parser
----------------------------------------