首先看lua虚拟机的创建函数
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud)
参数lua_Alloc f便是lua虚拟机的内存管理函数,虚拟机需要的内存创建回收都是通过这个函数实现。
这个函数的原型是:
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
其中的ud就是创建lua虚拟机时的第二个参数
我们一般不使用lua_newstate直接创建虚拟机,而是使用一个辅助函数
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) lua_atpanic(L, &panic);
return L;
}
这个辅助函数提供了内存操作的一个缺省实现
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == 0) {
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
这个缺省实现的功能是:
1.如果nsize是0则实现内存释放功能
2.重新调整指针所指向内存空间的大小,可以扩展也可以收缩
lua虚拟机在操作内存时,并没有直接使用这个函数,而是对其进行了一系列包装:
luaM_reallocv -> luaM_realloc_ -> l_alloc
luaM_freemem -> luaM_realloc_ -> l_alloc
luaM_free -> luaM_realloc_ -> l_alloc
luaM_freearray -> luaM_realloc_ -> l_alloc
luaM_malloc -> luaM_realloc_ -> l_alloc
luaM_new -> luaM_malloc -> luaM_realloc_ -> l_alloc
luaM_newvector -> luaM_reallocv -> luaM_realloc_ -> l_alloc
luaM_newobject -> luaM_realloc_ -> l_alloc
luaM_growvector -> luaM_growaux_ -> luaM_reallocv -> luaM_realloc_ -> l_alloc
这些函数最终都会使用luaM_realloc_ 进行内存分配
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
void *newblock;
global_State *g = G(L);
size_t realosize = (block) ? osize : 0;
lua_assert((realosize == 0) == (block == NULL));
#if defined(HARDMEMTESTS)
if (nsize > realosize && g->gcrunning)
luaC_fullgc(L, 1); /* force a GC whenever possible */
#endif
newblock = (*g->frealloc)(g->ud, block, osize, nsize);
if (newblock == NULL && nsize > 0) {
lua_assert(nsize > realosize); /* cannot fail when shrinking a block */
if (g->version) { /* is state fully built? */
luaC_fullgc(L, 1); /* try to free some memory... */
newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
}
if (newblock == NULL)
luaD_throw(L, LUA_ERRMEM);
}
lua_assert((nsize == 0) == (newblock == NULL));
g->GCdebt = (g->GCdebt + nsize) - realosize;
return newblock;
}
这段代码的大致意思就是使用l_alloc 进行内存分配,如果分配失败则进行一次完全gc后再分配一次,如果还是失败则抛出异常。