找回密码
 立即注册
查看: 178|回复: 0

lua 弱引用有什么用以及在xlua中的应用

[复制链接]
发表于 2022-10-10 19:59 | 显示全部楼层 |阅读模式
一、简单介绍
1、lua的GC默认是自动回收的,当一个对象的引用计数为0时,它就会被GC所回收。
2、lua中的表默认是强引用的,当你把某个对象放入表中时,就是生成一个对它的强引用(对象的引用计数+1),在对象的引用计数没有为0之前不会被GC回收;
3、如果把一个表声明为弱引用,则当把某个对象放如表中时,生成一个弱引用(对象不会被引用计数,可以理解为引用计数+0);如果一个对象只被弱引用表所引用(对象的引用计数为0),则会被下一次GC自动回收
所以弱引用表weak table的用途一般都是出于GC考虑的
注意:以上所指对象不包括值类型:number、boolean

二、使用
可以通过重写元方法__mode来定义一个表为弱引用表weak table,它包含三个值:"k"、"v"、"kv",如下所示:
k   标记表的key为弱引用
v   标记表的value为弱引用
kv  同时标记表的key和value为弱引用

三、例子
local defaults = {}
setmetatable(defaults, {__mode='v'})
local value1 = {}
local value2 = {}
defaults["key1"] = value1
defaults["key2"] = value2

value2 = nil
collectgarbage()

for k,v in pairs(defaults) do
    print(k,'\t',v)
end
执行上述代码后输出如下
key1            table: 0x600003b90900

设置弱引用后,以 value2 为值的数据从defaults中被垃圾回收了。

如果屏蔽setmetatable(defaults, {__mode='k'})  ,执行代码输出如下:
key2            table: 0x600000bf8880
key1            table: 0x600000bf8900

四、在xlua中 lua调用C#对象是如何保证对象不会回收的?又是什么时机释放的呢?
C#和Lua都是有自动垃圾回收机制的,并且相互是无感知的。如果传递到Lua的C#对象被C#自动回收掉了,而Lua这边仍毫不知情继续使用,则必然会导致无法预知的错误。所以基本原则是传递到Lua的C#对象,C#不能自动回收,只能Lua在确定不再使用后通知C#进行回收

为了保证C#不会自动回收对象,所有传递给Lua的对象都会被objects保持引用。真实传递给Lua的对象索引就是对象在objects中的索引

Lua这边为对象索引建立的userdata会被保存在缓存表中,而缓存表的引用模式被设置为弱引用

// ObjectTranslator.cs
LuaAPI.lua_newtable(L);  // 创建缓存表
LuaAPI.lua_newtable(L);  // 创建元表
LuaAPI.xlua_pushasciistring(L, "__mode");
LuaAPI.xlua_pushasciistring(L, "v");
LuaAPI.lua_rawset(L, -3);  // 元表[__mode] = v,表示这张表的所有值皆为弱引用
LuaAPI.lua_setmetatable(L, -2);  // 为缓存表设置元表
cacheRef = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX);
当Lua这边不再引用这个userdata时,userdata会被从缓存表中移除,Lua GC时会回收这个userdata,回收之前又会调用userdata元表的__gc方法,以此来通知C#层对C#对象的引用,这时C#对象就会再下一次垃圾回收时被回收掉
在BeginObjectRegister方法内部,会为userdata的元表添加__gc方法

// Utils.cs BeginObjectRegister方法
if ((type == null || !translator.HasCustomOp(type)) && type != typeof(decimal))
{
    LuaAPI.xlua_pushasciistring(L, "__gc");
    LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta);
    LuaAPI.lua_rawset(L, -3);  // 为元表设置__gc方法
}
translator.metaFunctions.GcMeta实际上就是StaticLuaCallbacks的LuaGC方法
// StaticLuaCallbacks.cs
[MonoPInvokeCallback(typeof(LuaCSFunction))]
public static int LuaGC(RealStatePtr L)
{
    try
    {
        int udata = LuaAPI.xlua_tocsobj_safe(L, 1);
        if (udata != -1)
        {
            ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
            if ( translator != null )
            {
                translator.collectObject(udata);
            }
        }
        return 0;
    }
    catch (Exception e)
    {
        return LuaAPI.luaL_error(L, "c# exception in LuaGC:" + e);
    }
}
LuaGC方法又会调用collectObject方法。在collectObject方法内部会将对象从objects移除,从而使对象不再被固定引用,能够被C# GC正常回收
// ObjectTranslator.cs
internal void collectObject(int obj_index_to_collect)
{
    object o;
   
    if (objects.TryGetValue(obj_index_to_collect, out o))
    {
        objects.Remove(obj_index_to_collect);
        
        if (o != null)
        {
            int obj_index;
            //lua gc是先把weak table移除后再调用__gc,这期间同一个对象可能再次push到lua,关联到新的index
            bool is_enum = o.GetType().IsEnum();
            if ((is_enum ? enumMap.TryGetValue(o, out obj_index) : reverseMap.TryGetValue(o, out obj_index))
                && obj_index == obj_index_to_collect)
            {
                if (is_enum)
                {
                    enumMap.Remove(o);
                }
                else
                {
                    reverseMap.Remove(o);
                }
            }
        }
    }
}

以上就是弱引用在xlua中的应用,希望对大家有所帮助。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-25 00:48 , Processed in 0.089543 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表