1:tolua++如何管理对象的生命周期
一般情况下,当lua里对c++对象的引用变量可以被垃圾回收时,tolua++只是简单的释放userdata占用的4字节指针地址内存。但是也可以通过绑定或者代码指定的方式,让tolua++真正释放对象所占内存。
绑定的方式,是指在将c++类型构造函数使用tolua++导出到lua里时,tolua++会自动生成new_local方法。如果在lua代码里,用这个方法新建对象时,tolua++会调用tolua_register_gc方法,指明回收对象时回收对象内存。
在c++代码里,使用tolua_pushusertype_and_takeownership;在lua代码里,使用tolua.takeownership,都可以达到同样的目的。
对于这些指定由tolua++回收内存的对象,如果其类型的析构函数也通过tolua++导出了,则在回收内存时,会通过delete运算符,调用对象的析构函数。否则只会使用free方法回收。
tolua_register_gc方法,做的事情,是以对象指针为键,以对象metatable为值,将键值对存储在tolua_gc表里。在对象类型的metatable表的__gc方法里,tolua++会检查tolua_gc表是否包含以这个地址为键的表项。包含的话才会进行上述的内存回收工作。
2:userdata的回收
Lua 提供了一个自动的内存管理。这就是说你不需要关心创建新对象的分配内存操作,也不需要在这些对象不再需要时的主动释放内存。 Lua 通过运行一个垃圾收集器来自动管理内存,以此一遍又一遍的回收死掉的对象(这是指 Lua 中不再访问的到的对象)占用的内存。 Lua 中所有对象都被自动管理,包括: table, userdata、 函数、线程、和字符串。
Lua 实现了一个增量标记清除的收集器。它用两个数字来控制垃圾收集周期: garbage-collector pause 和 garbage-collector step multiplier 。
garbage-collector pause 控制了收集器在开始一个新的收集周期之前要等待多久。随着数字的增大就导致收集器工作工作的不那么主动。小于 1 的值意味着收集器在新的周期开始时不再等待。当值为 2 的时候意味着在总使用内存数量达到原来的两倍时再开启新的周期。
step multiplier 控制了收集器相对内存分配的速度。更大的数字将导致收集器工作的更主动的同时,也使每步收集的尺寸增加。小于 1 的值会使收集器工作的非常慢过在 C 中调用 lua_gc 或是在 Lua 中调用 collectgarbage 来改变这些数字。两者都接受百分比数值(因此传入参数 100 意味着实际值 1 )。通过这些函数,你也可以直接控制收集器(例如,停止或是重启)。
使用 C API ,你可以给 userdata 设置一个垃圾收集的元方法。这个元方法也被称为结束子。结束子允许你用额外的资源管理器和 Lua 的内存管理器协同工作(比如关闭文件、网络连接、或是数据库连接,也可以说释放你自己的内存)。
一个 userdata 可被回收,若它的 Metatable 中有 __gc 这个域 ,垃圾收集器就不立即收回它。取而代之的是,Lua 把它们放到一个列表中。最收集结束后,Lua 针对列表中的每个 userdata 执行了下面这个函数的等价操作:
function gc_event (udata) local h = metatable(udata).__gc
if h then,可能导致收集器永远都结束不了当前周期。缺省值为 2 ,这意味着收集器将以内存分配器的两倍速运行。
你可以通
h(udata) end end
在每个垃圾收集周期的结尾,每个在当前周期被收集起来的 userdata 的结束子会以它们构造时的逆序依次调用。也就是说,收集列表中,最后一个在程序中被创建的 userdata 的结束子会被第一个调用。
3:代码
{
//...
push_collector(L, name, col);
//...
/* now we also need to store the collector table for the const
instances of the class */
push_collector(L, cname, col);
}
{
//..
lua_pushstring(L,"__gc");
lua_pushstring(L, "tolua_gc_event");
lua_rawget(L, LUA_REGISTRYINDEX);
lua_rawset(L,-3);
}
lua_pushstring(L, "tolua_gc_event");
lua_pushstring(L, "tolua_gc");
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushstring(L, "tolua_super");
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushcclosure(L, class_gc_event, 2);
lua_rawset(L, LUA_REGISTRYINDEX);
TOLUA_API int class_gc_event (lua_State* L) 一个闭包函数,还有两个upvalue。
{
void* u = *((void**)lua_touserdata(L,1));
int top;
lua_pushvalue(L, lua_upvalueindex(1));--->tolua_gc表
lua_pushlightuserdata(L,u);
lua_rawget(L,-2); /* stack: gc umt */ 获取tolua_gc表中userdata的metatable,gc[userdata] = metatable
lua_getmetatable(L,1); /* stack: gc umt mt */
/*fprintf(stderr, "checking type\n");*/
top = lua_gettop(L);
if (tolua_fast_isa(L,top,top-1, lua_upvalueindex(2))) /* make sure we collect correct type */
{
/*fprintf(stderr, "Found type!\n");*/
/* get gc function */
lua_pushliteral(L,".collector"); 检查.collector域是否是一个函数
lua_rawget(L,-2); /* stack: gc umt mt collector */
if (lua_isfunction(L,-1)) {
/*fprintf(stderr, "Found .collector!\n");*/
}
else {
lua_pop(L,1);
/*fprintf(stderr, "Using default cleanup\n");*/
lua_pushcfunction(L,tolua_default_collect);
}
lua_pushvalue(L,1); /* stack: gc umt mt collector u */
lua_call(L,1,0); 调用注册类的时候写入.collector域的函数。
lua_pushlightuserdata(L,u); /* stack: gc umt mt u */
lua_pushnil(L); /* stack: gc umt mt u nil */
lua_rawset(L,-5); /* stack: gc umt mt */ 将userdata从tolua_gc表中移除
}
lua_pop(L,3);
return 0;
}
{
int success = 1;
void *value = *(void **)lua_touserdata(L,lo);
lua_pushstring(L,"tolua_gc");
lua_rawget(L,LUA_REGISTRYINDEX);
lua_pushlightuserdata(L,value);
lua_rawget(L,-2);
if (!lua_isnil(L,-1)) /* make sure that object is not already owned */
success = 0;
else
{
lua_pushlightuserdata(L,value);
lua_getmetatable(L,lo);
lua_rawset(L,-4);
}
lua_pop(L,2);
return success;
}