先来看一下 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
----------------------------------------