tolua++内存释放坑

前言

本来想参考下tolua++的对象生命周期维护方式。一不小心发现了一个坑。

代码追踪

我这里用得是tolua++ 1.0.93版本。

tolua++在new一个类的时候,会把类指针作为userdata传入lua,建立metatable并通过tolua_classevents函数给metatable注册魔术方法。

这里面可以看到gc方法被设成了 _G.tolua_gc_event

    lua_pushstring(L,"__gc");
    lua_pushstring(L, "tolua_gc_event");
    lua_rawget(L, LUA_REGISTRYINDEX);
    /*lua_pushcfunction(L,class_gc_event);*/
    lua_rawset(L,-3);

那么关键是这个_G.tolua_gc_event被设成了什么,在tolua_open函数里可以找到

        /* create gc_event closure */
        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);

垃圾回收函数使用的是class_gc_event并且添加了两个闭包参数_G.tolua_gc,_G.tolua_super。其中前一个用于设置全局metatable缓存。后一个貌似是基类。

比如新建一个Class, 指针是p,lua对象是t。那么相当于在lua里会设置

_G.tolua_gc[p] = getmetatable(t)

具体见tolua_register_gc函数

TOLUA_API int class_gc_event (lua_State* L)
{
    void* u = *((void**)lua_touserdata(L,1));
    int top;
    /*fprintf(stderr, "collecting: looking at %p\n", u);*/
    /*
    lua_pushstring(L,"tolua_gc");
    lua_rawget(L,LUA_REGISTRYINDEX); 
    */
    lua_pushvalue(L, lua_upvalueindex(1)); // 这里是拿到 _G.tolua_gc
    lua_pushlightuserdata(L,u);
    lua_rawget(L,-2);            /* stack: gc umt    */ // 这里是拿到上文中提到的 _G.tolua_gc[p]
    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");
        lua_rawget(L,-2);           /* stack: gc umt mt collector */
        if (lua_isfunction(L,-1)) { // .collector有数据时调用.collector函数
            /*fprintf(stderr, "Found .collector!\n");*/
        }
        else { // .collector没有数据时调用tolua_default_collect函数
            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); // 这里开始调用tolua_default_collect函数

        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 */
    }
    lua_pop(L,3);
    return 0;
}

然后坑爹的就来了tolua_default_collect这么实现的

/* Default collect function
*/
TOLUA_API int tolua_default_collect (lua_State* tolua_S)
{
    void* self = tolua_tousertype(tolua_S,1,0);
    free(self);
    return 0;
}

这个很2B的坑

这么搞问题就来了,默认tolua++是没有设置.collector函数的(new一个自定义class之后调用push_collector的传入了空指针),然后释放的时候就华丽丽free掉了,且不说标准C++并不保证new的分配内存和malloc一样(虽然现在大部分编译器的实现确实一样),它竟然没调用析构函数。这意味着如果类里面有使用stl或者其他依赖析构来释放资源的成员类对象的话,就华丽丽地内存泄露了。

另外,网上随便搜了一下,也找到其他人也有发现这个问题。http://www.cnblogs.com/egmkang/archive/2012/07/01/2572064.html

无力吐槽了,让我晕一会…

Written with StackEdit.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏抠抠空间

ECMAScript简介以及es6新增语法

920
来自专栏java一日一条

在Java中如何避免“!=null”式的判空语句?

我整天都是在跟Java打交道。我在Java开发中最常用的一段代码就是用object != null在使用对象之前判断是否为空。这么做是为了避免NullPoint...

1412
来自专栏逸鹏说道

必备 .NET - C# 异常处理

欢迎查看首个“必备.NET”专栏。您可以在其中了解 Microsoft .NET Framework 领域的所有最新动态,无论是 C# vNext 的最新进展(...

2786
来自专栏java达人

Java中的堆和栈的区别

当一个人开始学习Java或者其他编程语言的时候,会接触到堆和栈,由于一开始没有明确清晰的说明解释,很多人会产生很多疑问,什么是堆,什么是栈,堆和栈有什么区别?更...

2316
来自专栏Petrichor的专栏

tensorflow编程: Building Graphs

每次都必须要指定一个graph作为as_default,并只能在该graph中进行一切操作。

1453
来自专栏Lambda

Java8新日期处理API

Java8引入了一套全新的时间日期API,本篇随笔将说明学习java8的这套API。 java.time包中的是类是不可变且线程安全的。新的时间及日期API位...

42810
来自专栏日常分享

JSON 数据格式

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的...

5322
来自专栏木可大大

初识Java

为了让Java能够实现跨平台,Java的发明者们增加了一个抽象层,即JVM(Java Virtual Machine,Java虚拟机),自定义一套指令并且和硬件...

4584
来自专栏java一日一条

在Java中如何避免“!=null”式的判空语句?

我整天都是在跟Java打交道。我在Java开发中最常用的一段代码就是用object != null在使用对象之前判断是否为空。这么做是为了避免NullPoint...

1991
来自专栏二进制文集

Java编程思想 ——对象导论

所有编程语言都是抽象机制。人们所能够解决的问题的复杂性,直接取决于抽象的类型和数量。汇编语言是对底层机器的轻微抽象;“命令式语言”(BASIC、C)是对汇编语言...

1363

扫码关注云+社区

领取腾讯云代金券