lua内存管理
本文内容基于版本:Lua 5.3.0
Lua内存管理器规则
Lua允许用户自定义内存管理器,并在创建Lua虚拟机(lua_State实例)时传入。当然自定义内存管理器必须遵循Lua已定义的一些行为规则。创建一个Lua虚拟机需要使用luaL_newstate函数:
lua_State *L = luaL_newstate();
luaL_newstate函数的实现主要是调用lua_newstate函数,lua_newstate函数将接受一个内存分配器函数作为参数,进而在内部分配内存:
// lauxlib.h
LUALIB_API lua_State *(luaL_newstate) (void); // lauxlib.c
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) lua_atpanic(L, &panic);
return L;
} // lua.h
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); // lstate.c
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
if (l == NULL) return NULL;
......
return L;
}
可以看到,Lua内存分配器必须是一个lua_Alloc类型的函数,如果想自定义内存分配器,那么用户必须定义一个lua_Alloc类型的函数:
// lua.h
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
Lua默认内存管理器
luaL_newstate函数调用lua_newstate函数时,传入了一个lua_Alloc类型的函数:l_alloc。该函数就是Lua提供的默认内存管理函数,它主要使用C标准库中的realloc函数进行内存管理:
// lauxlib.c
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == ) {
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
ud :Lua默认内存管理器并未使用该参数。不过在用户自定义内存管理器中,可以让内存管理在不同的堆上进行。
ptr :非NULL表示指向一个已分配的内存块指针,NULL表示将分配一块nsize大小的新内存块。
osize:原始内存块大小,默认内存管理器并未使用该参数。Lua的设计强制在调用内存管理器函数时候需要给出原始内存块的大小信息,如果用户需要自定义一个高效的内存管理器,那么这个参数信息将十分重要。这是因为大多数的内存管理算法都需要为所管理的内存块加上一个cookie,里面存储了内存块尺寸的信息,以便在释放内存的时候能够获取到尺寸信息(譬如多级内存池回收内存操作)。而Lua内存管理器刻意在调用内存管理器时提供了这个信息,这样就不必额外存储这些cookie信息,这样在大量使用小内存块的环境中将可以节省不少的内存。另外在ptr传入NULL时,osize表示Lua对象类型(LUA_TNIL、LUA_TBOOLEAN、LUA_TTHREAD等等),这样内存管理器就可以知道当前在分配的对象的类型,从而可以针对它做一些统计或优化的工作。
nsize:新的内存块大小,特别地,在nsize为0时需要提供内存释放的功能。
global_State.frealloc
我们已经知道在创建Lua虚拟机时将传入一个内存管理器,然后使用该内存管理器分配相关的数据结构。Lua设计了一个global_State结构(全局状态机)来存储各种全局数据,其中就包含了内存管理器。也就是说我们使用内存管理器创建了一个Lua虚拟机,Lua虚拟机的全局状态机中保存了该内存管理器,以便Lua后续内部的工作能使用该内存管理器进行内存管理工作。
// lstate.c
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
if (l == NULL) return NULL;
L = &l->l.l;
g = &l->g;
L->next = NULL;
L->tt = LUA_TTHREAD;
g->currentwhite = bitmask(WHITE0BIT);
L->marked = luaC_white(g);
preinit_thread(L, g);
g->frealloc = f;
...... return L;
}
Lua内存管理的宏
Lua设计了一组宏来管理不同类别的内存:单个对象、数组、可变长数组等等。这一系列的宏使用了两个核心API:luaM_realloc_和luaM_growaux_,下面我们先就这两个核心API进行分析。
• luaM_realloc
luaM_realloc_函数并不会被直接调用,它将调用保存在global_State.frealloc中的内存分配器进行内存管理工作。luaM_realloc_会根据传入的osize和nsize调整内部感知的内存大小(设置GCdebt),并在内存不够用的时候会主动尝试做GC操作。
// lmem.h
/* not to be called directly */
LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
size_t size); // lmem.c
/*
** generic allocation routine.
*/
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 : ;
lua_assert((realosize == ) == (block == NULL));
#if defined(HARDMEMTESTS)
if (nsize > realosize && g->gcrunning)
luaC_fullgc(L, ); /* force a GC whenever possible */
#endif
newblock = (*g->frealloc)(g->ud, block, osize, nsize);
if (newblock == NULL && nsize > ) {
api_check( nsize > realosize,
"realloc cannot fail when shrinking a block");
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 == ) == (newblock == NULL));
g->GCdebt = (g->GCdebt + nsize) - realosize;
return newblock;
}
• luaM_growaux_
luaM_growaux_函数是用来管理可变长数组的,其主要策略是:当数组空间不够时,扩大为原来空间的两倍。其中管理内存部分使用了基于luaM_realloc_函数的宏luaM_reallocv,该宏针对数组操作,根据新的数组元素个数重新分配内存。
// lmem.h
/* not to be called directly */
LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,
size_t size_elem, int limit,
const char *what); // lmem.c
#define MINSIZEARRAY 4 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/) { /* 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;
}
size :数组长度(最大容纳元素个数),传入表示原始数组长度,传出表示重新分配后数组长度;
size_elems:单个数组元素大小;
limit :数组元素最大容纳个数限制;
what :提示信息字符串;
• luaM_reallocv
/*
** This macro reallocs a vector 'b' from 'on' to 'n' elements, where
** each element has size 'e'. In case of arithmetic overflow of the
** product 'n'*'e', it raises an error (calling 'luaM_toobig'). Because
** 'e' is always constant, it avoids the runtime division MAX_SIZET/(e).
**
** (The macro is somewhat complex to avoid warnings: The 'sizeof'
** comparison avoids a runtime comparison when overflow cannot occur.
** The compiler should be able to optimize the real test by itself, but
** when it does it, it may give a warning about "comparison is always
** false due to limited range of data type"; the +1 tricks the compiler,
** avoiding this warning but also this optimization.)
*/
#define luaM_reallocv(L,b,on,n,e) \
(((sizeof(n) >= sizeof(size_t) && cast(size_t, (n)) + > MAX_SIZET/(e)) \
? luaM_toobig(L) : cast_void()) , \
luaM_realloc_(L, (b), (on)*(e), (n)*(e)))
luaM_reallocv将使数组b的长度(最大容纳元素个数)从on重新分配为n,其中每个数组元素大小为e。
b :数组指针;
on :数组重新分配前的长度(最大容纳元素个数);
n :数组重新分配后的长度(最大容纳元素个数);
e :数组元素大小;
• luaM_reallocvchar
/*
** Arrays of chars do not need any test
*/
#define luaM_reallocvchar(L,b,on,n) \
cast(char *, luaM_realloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char)))
luaM_reallocvchar将使字符数组b的长度(最大容纳元素个数)从on重新分配为n,其中每个数组元素大小为sizeof(char)。
b :数组指针;
on :数组重新分配前的长度(最大容纳元素个数);
n :数组重新分配后的长度(最大容纳元素个数);
• luaM_freemem
#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
luaM_freemem将释放b指向的内存块空间。
b :内存块指针;
s :内存块大小;
• luaM_free
#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
luaM_free将释放b指向的内存块空间(b表示某种对象类型指针)。
b :内存指针,同时表示某种对象类型指针;
• luaM_freearray
#define luaM_freearray(L, b, n) luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0)
luaM_freearray将释放b指向的内存块空间(b表示某种类型对象的数组指针)。
b :内存指针,同时表示某种类型对象的数组指针;
n :数组长度(最大容纳元素个数);
• luaM_malloc
#define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s))
luaM_malloc将分配一块大小为s的内存块空间。
s :将要分配的内存块空间大小;
• luaM_new
#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
luaM_new将分配一块内存块空间,空间大小为sizeof(t)。
t :某种数据类型;
• luaM_newvector
#define luaM_newvector(L,n,t) \
cast(t *, luaM_reallocv(L, NULL, , n, sizeof(t)))
luaM_newvector将分配一个长度为n的数组空间,数组元素为类型t。
n :数组长度(最大容纳元素个数);
t :数组元素类型;
• luaM_newobject
#define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s))
luaM_newobject将分配一块大小为s的内存块空间,其将要容纳的Lua数据类型为tag表示的类型。
tag :Lua数据类型;
s :分配的内存块大小;
• luaM_growvector
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
if ((nelems)+ > (size)) \
((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
luaM_growvector将在数组空间不足以容纳下一个元素的情况下增长空间大小(原空间大小 * 2)。
v :数组指针;
nelems :正在使用的元素个数;
size :数组元素个数,传入表示原始数组大小,传出表示重新分配后数组大小;
t :(数组元素的)数据类型;
limit :数组元素最大个数限制;
e :提示信息字符串;
• luaM_reallocvector
#define luaM_reallocvector(L, v,oldn,n,t) \
((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
luaM_reallocvector将重新分配数组空间大小。
v :数组指针;
oldn :重新分配前数组大小;
n :重新分配后数组大小;
lua内存管理的更多相关文章
- Lua的内存管理
[前言] 在历史长河中,各种各样的新语言,总是伴随着我们编程人员:有的时候,工作的需要,我们不得不去学习这些很炫的,很新的语言.学习任何一门语言(我这里只说学习),都无非就是学习那么几个大模块,基本语 ...
- 【Cocos2d-x 3.x】内存管理机制与源码分析
侯捷先生说过这么一句话 : 源码之前,了无秘密. 要了解Cocos2d-x的内存管理机制,就得阅读源码. 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Coco ...
- cocos2d-x之内存管理(4)
c++的内存管理一直以来都是个问题,也有多种实现方案,比如智能指针,使用引用计数等,cocos2d-x也需要涉及到内存的管理. cocos2d-x是如何管理内存的呢? cocos2d-x的内存管理主要 ...
- 再谈LRU双链表内存管理
N年前我写了个双链表也发了博客,还添了代码.但是那个代码不但复杂,而且还有有问题的,一直懒得整理,放在空间误导别人.最近在写服务端,今天抽点空补一篇. 关于LRU网上随便搜,有过后端经验的人应该很多都 ...
- Cocos2d-x内存管理研究<二>
http://hi.baidu.com/tzkt623/item/46a26805adf7e938a3332a04 上一篇我们讲了内核是如何将指针加入管理类进行管理.这次我将分析一下内核是如何自动 ...
- Cocos2d-X内存管理研究<一>
http://hi.baidu.com/tzkt623/item/651ca7d7a0aff6e055347f67 半夜没事干,研究内核,作为我cocos2d-x的第一篇教程.cocos ...
- GC与显式内存管理
C++复兴的话题至今已被鼓吹两年有余,Herb Sutter和Bjarne Stroustrup等大牛们也为C++带来了大步伐的革新.然而,从这两年的效果而言,C++的复兴并没有发生.一方面随着世界经 ...
- Lua如何管理”package”
Lua如何管理"package" 方式一: 私有方法和变量都需要显式定义为local类型的,这很容易造成错误.一旦不小心漏写,就又将方法定义为全局的了. "package ...
- Lua内存分析工具
最近给公司写了一个lua内存分析工具,可以非常方便的分析出Lua内存泄露问题,有图形化界面操作,方便手机端上传快照等功能 内存分析我是在c语言端写的,也有人写过lua端的分析工具,也蛮好用的,不过lu ...
随机推荐
- 学习Struts框架系列(三):声明式异常处理
在Struts1.X的版本中加入了对异常的处理Exception Handler,有了它我们可以不使用try/catch捕获异常,一旦出现了我们已经定义的异常,那么就会转到相应的页面,并且携带异常信息 ...
- cordova / Ionic 开发问题汇总
cordova / Ionic 开发问题汇总 1. 导入工程的"The import android cannot be resolved"错误解决方法 2. MainActivi ...
- MySQL查看表结构三种方法
1:desc T1 2:EXPLAIN T1 3:SHOW COLUMNS FROM T1
- 建maven私服nexus
1.下载nexus 2.配置java环境 3.安装 C:\Program Files\nexus\nexus-2.11.4-01\bin\jsw\windows-x86-64 4.配置 http:// ...
- 微服务(Microservices)
说在前面 好久没写博文了,心里痒痒(或许是换工作后,有点时间了吧). 近期好像谈论微服务的人比較多,也開始学习一下.可是都有E文.看起来半懂不懂的. Martinfowler的< ...
- Swift教程_swift常见问题(0005)_完美解决Cannot override 'dealloc'异常
Swift教程_swift常见问题(0001)_CoreData: warning: Unable to load class named 'xxx' for entity 'xxx' Swift教程 ...
- isearch5 index,attribute和summary。
索引 isearch5 支持的索引分为:index,attribute和summary. Index指的是倒排索引,它存储了存储了从term到DocID的映射关系,形如: term-->(Doc ...
- LR函数基础(一)
函数用到:web_reg_find(). lr_log_message(). lr_eval_string().strcmp().atoi() Action(){ web_reg_find(&q ...
- Oracle-client支持exp|imp|rman
官方精简版的驱动,不支持持exp/imp/rman,故需要安装oracle_client客户端. 实验环境: Centos6.5 x64 Oracle 11.2.0.4.0 Oracle_clie ...
- Dubbo Monitor 配置
1. Dubbo Monitor 下载dubbo-monitor-simple-2.5.3-assembly.tar.gz 链接:http://pan.baidu.com/s/1gf88wDX 密码: ...