目录
一、Mem内存操作 - 核心分配函数g->frealloc
二、Mem内存操作 - 分配内存luaM_malloc
三、Mem内存操作 - 释放内存luaM_free
四、Mem内存操作 - 内存扩容luaM_growvector
原先以为Lua的内存操作也是高大上的,但是仔细研究了一下,突然发现,这块代码绕来绕去,封装来封装去,有一些low low感。
废话少说,直接上原理吧。这部分的代码,我也不画详细的图了。
一、Mem内存操作 - 核心分配函数g->frealloc
Lua的全局状态机里面,有两行代码,定义了内存分配的基础函数。底层的内存分配函数主要调用了c语言本身的内存分配函数(malloc、free、realloc等)。想要研究linux系统的内存分配器的,可以看我这边文章《Linux c 开发 - 内存管理器ptmalloc》
先看一下全局状态机里面的定义:
/*
** 'global state', shared by all threads of this state
** lua 全局状态机
** 作用:管理全局数据,全局字符串表、内存管理函数、 GC 把所有对象串联起来的信息、内存等
*/
typedef struct global_State {
/* 版本号 */
const lua_Number *version; /* pointer to version number */
/* 内存管理 */
lua_Alloc frealloc; /* Lua的全局内存分配器,用户可以替换成自己的 - function to reallocate memory */
void *ud; /* 分配器的userdata 貌似没用到- auxiliary data to 'frealloc' */
将l_alloc函数对象赋值到g->frealloc。
- l_alloc函数中,当nsize分配内存大小为0,则调用free进行释放
- 当nsize分配内存大小不为零,则调用realloc方法分配一块内存
- Lua没有自己的内存分配管理器,依赖于操作系统本身的内存分配器。所以就必须注意,内存频繁分配造成的内存碎片增加。
/**
* 内存分配基础函数
*/
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); //分配内存
}
/**
* 创建一个全局状态机
*/
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL); //将l_alloc函数对象赋值到g->frealloc
if (L) lua_atpanic(L, &panic);
return L;
}
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
/* 分配一块lua_State结构的内容块 */
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
g->frealloc = f; //赋值
二、Mem内存操作 - 分配内存luaM_malloc
内存分配的具体操作主要在文件lmem.h和lmem.c文件中。
内存分配底层,是调用*g->frealloc实现(即l_alloc函数)
/**
* 内存分配函数
*/
#define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s)) //直接分配一块固定大小的内存
#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) //分配一个固定类型大小的内存
#define luaM_newvector(L,n,t) \
cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) //分配多个固定类型的内容块,例如Table 的array
#define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s)) //分配一个固定类型的对象
/*
** generic allocation routine.
** 内存分配函数
** 内容快最终会调用*g->frealloc(*l_alloc)函数处理
**
** osize = 老的内存块大小 这参数没用上
**
*/
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;
}
三、Mem内存操作 - 释放内存luaM_free
释放内存比较简单,调用luaM_realloc_,nsize设置为0。
当调用底层l_alloc函数的时候,回去判断nsize=0的时候,调用操作系统的free函数自动回收内存
/* 内存释放操作 */
#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
#define luaM_freearray(L, b, n) luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0)
四、Mem内存操作 - 内存扩容luaM_growvector
底层内存分配使用了realloc方法,所以填入支持扩容操作。
扩容规则:
- 扩容不能超过limit限制,如果超过了,则扩容到limit
- 一般扩容按照*2系数去扩
/* 内存扩容 */
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
if ((nelems)+1 > (size)) \
((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
/**
* 内存扩容
* 1. 扩容不能超过limit限制,如果超过了,则扩容到limit
* 2. 一般扩容按照*2系数去扩
*/
void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
int limit, const char *what) {
void *newblock;
int newsize;
if (*size >= limit/2) { /* cannot double it? */
if (*size >= limit) /* cannot grow even a little? */
luaG_runerror(L, "too many %s (limit is %d)", what, limit);
newsize = limit; /* still have at least one free place */
}
else {
newsize = (*size)*2;
if (newsize < MINSIZEARRAY)
newsize = MINSIZEARRAY; /* minimum size */
}
newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
*size = newsize; /* update only when everything else is OK */
return newblock;
}
l_noret luaM_toobig (lua_State *L) {
luaG_runerror(L, "memory allocation error: block too big");
}
所以,通篇看下来,感觉这部分代码还是有点鸡肋。封装了很多层,不是特别友好。