xLua源码解析——值类型传递无GC
概述:xLua的FAQ里:
值类型传递会有gc alloc么?
如果你使用的是delegate调用lua函数,或者用LuaTable、LuaFunction的无gc接口,或者数组的话,以下值类型都是没gc的:
1、所有的基本值类型(所有整数,所有浮点数,decimal);
2、所有的枚举类型;
3、字段只包含值类型的struct,可嵌套其它只包含值类型struct;
其中2、3需要把该类型加到GCOptimize。xLua示例Examples->05_NoGc->NoGc.cs展示怎么去避免值类型的GC。下面分析一下,为什么会产生GC,以及xLua如何解决这个问题。
为什么会产生GC
通常来说,只要堆分配了内存,也就是实例化引用对象,在对象使用完时,就会被GC。所以值类型时从栈上分配的,原则上不会产生GC,但是在lua和C#交互过程中,值传递会因为装箱拆箱产生GC。
如何解决
以MyStruct为例:
public struct MyStruct
{
public MyStruct(int p1, int p2)
{
a = p1;
b = p2;
c = p2;
e.c = (byte)p1;
}
public int a;
public int b;
public decimal c;
public Pedding e;
}
看下面的MyStruct的部分生成代码(打上了GCOptimize标签)。
// XLuaTest.MyStruct生成代码
static int __CreateInstance(RealStatePtr L)
{
try
{
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
if (LuaAPI.lua_gettop(L) == 3 && LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, 2) && LuaTypes.LUA_TNUMBER == LuaAPI.lua_type(L, 3))
{
int _p1 = LuaAPI.xlua_tointeger(L, 2);
int _p2 = LuaAPI.xlua_tointeger(L, 3);
var gen_ret = new XLuaTest.MyStruct(_p1, _p2);
// 有标签
// translator.Push(L, gen_ret);
// 无标签
translator.PushXLuaTestMyStruct(L, gen_ret);
return 1;
}
if (LuaAPI.lua_gettop(L) == 1)
{
// 有标签
// translator.Push(L, default(XLuaTest.MyStruct));
// 无标签
translator.PushXLuaTestMyStruct(L, default(XLuaTest.MyStruct));
return 1;
}
}
catch (System.Exception gen_e)
{
return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
}
return LuaAPI.luaL_error(L, "invalid arguments to XLuaTest.MyStruct constructor!");
}
如果没有打 标签,则translator.PushXLuaTestMyStruct(RealStatePtr L, XLuaTest.MyStruct val)替换为translator.Push(RealStatePtr L, object o)。
可以看出,通用的Push方法的参数是object,这里就会触发装箱的操作。
所以打上后,生成的代码主要就是多了Push、Get、Update三个函数的专用函数。相关代码如下:
int XLuaTestMyStruct_TypeID = -1;
public void PushXLuaTestMyStruct(RealStatePtr L, XLuaTest.MyStruct val)
{
if (XLuaTestMyStruct_TypeID == -1)
{
bool is_first;
XLuaTestMyStruct_TypeID = getTypeId(L, typeof(XLuaTest.MyStruct), out is_first);
}
IntPtr buff = LuaAPI.xlua_pushstruct(L, 25, XLuaTestMyStruct_TypeID);
if (!CopyByValue.Pack(buff, 0, val))
{
throw new Exception("pack fail fail for XLuaTest.MyStruct ,value=" + val);
}
}
public void Get(RealStatePtr L, int index, out XLuaTest.MyStruct val)
{
LuaTypes type = LuaAPI.lua_type(L, index);
if (type == LuaTypes.LUA_TUSERDATA)
{
if (LuaAPI.xlua_gettypeid(L, index) != XLuaTestMyStruct_TypeID)
{
throw new Exception("invalid userdata for XLuaTest.MyStruct");
}
IntPtr buff = LuaAPI.lua_touserdata(L, index); if (!CopyByValue.UnPack(buff, 0, out val))
{
throw new Exception("unpack fail for XLuaTest.MyStruct");
}
}
else if (type == LuaTypes.LUA_TTABLE)
{
CopyByValue.UnPack(this, L, index, out val);
}
else
{
val = (XLuaTest.MyStruct)objectCasters.GetCaster(typeof(XLuaTest.MyStruct))(L, index, null);
}
}
public void UpdateXLuaTestMyStruct(RealStatePtr L, int index, XLuaTest.MyStruct val)
{
if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA)
{
if (LuaAPI.xlua_gettypeid(L, index) != XLuaTestMyStruct_TypeID)
{
throw new Exception("invalid userdata for XLuaTest.MyStruct");
}
IntPtr buff = LuaAPI.lua_touserdata(L, index);
if (!CopyByValue.Pack(buff, 0, val))
{
throw new Exception("pack fail for XLuaTest.MyStruct ,value=" + val);
}
}
else
{
throw new Exception("try to update a data with lua type:" + LuaAPI.lua_type(L, index));
}
}
还有Pack和UnPack的方法:
public static void UnPack(ObjectTranslator translator, RealStatePtr L, int idx, out XLuaTest.MyStruct val)
{
val = new XLuaTest.MyStruct();
int top = LuaAPI.lua_gettop(L);
if (Utils.LoadField(L, idx, "a"))
{
translator.Get(L, top + 1, out val.a);
}
LuaAPI.lua_pop(L, 1);
if (Utils.LoadField(L, idx, "b"))
{
translator.Get(L, top + 1, out val.b);
}
LuaAPI.lua_pop(L, 1);
if (Utils.LoadField(L, idx, "c"))
{
translator.Get(L, top + 1, out val.c);
}
LuaAPI.lua_pop(L, 1);
if (Utils.LoadField(L, idx, "e"))
{
translator.Get(L, top + 1, out val.e);
}
LuaAPI.lua_pop(L, 1);
}
public static bool Pack(IntPtr buff, int offset, XLuaTest.MyStruct field)
{
if (!Pack(buff, offset, field.a))
{
return false;
}
if (!Pack(buff, offset + 4, field.b))
{
return false;
}
if (!Pack(buff, offset + 8, field.c))
{
return false;
}
if (!Pack(buff, offset + 24, field.e))
{
return false;
}
return true;
}
public static bool UnPack(IntPtr buff, int offset, out XLuaTest.MyStruct field)
{
field = default(XLuaTest.MyStruct);
if (!UnPack(buff, offset, out field.a))
{
return false;
}
if (!UnPack(buff, offset + 4, out field.b))
{
return false;
}
if (!UnPack(buff, offset + 8, out field.c))
{
return false;
}
if (!UnPack(buff, offset + 24, out field.e))
{
return false;
}
return true;
}
// 额外贴出具体的Pack方法
public static bool Pack(IntPtr buff, int offset, int field)
{
return LuaAPI.xlua_pack_int32_t(buff, offset, field);
}
总结
上面只举例了struct,其他LuaTable和LuaFunction的无GC方法等,也是同样的原理,通过生成专门的转换方法,避免使用通用方法的objcet类型参数引起的装拆箱。
页:
[1]