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

xlua重启虚拟机处理

[复制链接]
发表于 2021-8-14 13:31 | 显示全部楼层 |阅读模式
起因
xlua重启虚拟机时,需要调用Env.Dispose();
但会抛出异常"try to dispose a LuaEnv with C# callback!"
  1. if (!translator.AllDelegateBridgeReleased())
  2. {
  3.     throw new InvalidOperationException("try to dispose a LuaEnv with C# callback!");
  4. }
复制代码
这是因为C#端还持有lua的方法没有被释放,所以想要重启虚拟机必须要先把所有的C#委托清空
还需要注意
    在同一个方法中释放了delegate的引用,但又立即调用了LuaEnv.Dispose().在Lua端调用了Env.Dispose();
解决方法
UI部分的事件监听是大头,比如
  1. self._btnMapShift.onClick:AddListener(CT.MainCityCtrl.OnBtnMapShift)
  2. 我们可以使用正则表式式 把这种监听替换成统一的方法里处理,然后在方法,把所有的监听都记录下来,需要重启时统一释放
复制代码
统一处理UI的方法代码
  1. function UIAddListenerIndirect(uiEnlement,eventType,func)
  2.     if uiEnlement[eventType] then
  3.         local callInfo = {
  4.             uiEnlement = uiEnlement,
  5.             eventType = eventType,
  6.             callBackFunc = func
  7.         }
  8.         uiEnlement[eventType]:AddListener(func)
  9.         table.insert(D.UIListenerDelegate,callInfo)
  10.     end
  11. end
  12. function UIRemoveListenerIndirect(uiEnlement,eventType,func)
  13.     if uiEnlement[eventType] then
  14.         uiEnlement[eventType]:RemoveListener(func)
  15.     end
  16. end
  17. function UIRemoveAllListener()
  18.     for k,v in pairs(D.UIListenerDelegate) do
  19.         local callInfo = v
  20.         --callInfo.uiDelegate:RemoveListener(callInfo.callBackFunc)
  21.         if callInfo.uiEnlement[callInfo.eventType] then
  22.             callInfo.uiEnlement[callInfo.eventType]:RemoveAllListeners()
  23.             callInfo.uiEnlement[callInfo.eventType]:Invoke("",0)
  24.         end
  25.     end
  26.     D.UIListenerDelegate = {}
  27. end
复制代码
此外还有两个方法查看C#端没被清空的委托 便于我们单独处理
1 xlua提供了一个lua方法(该方法位于XLua/Resources/xlua/util.lua.txt中): print_func_ref_by_csharp()
  1. function print_func_ref_by_csharp()
  2.     local registry = debug.getregistry()
  3.     for k, v in pairs(registry) do
  4.         if type(k) == 'number' and type(v) == 'function' and registry[v] == k then
  5.             local info = debug.getinfo(v)
  6.             print(string.format('%s:%d', info.short_src, info.linedefined))
  7.         end
  8.     end
  9. end
复制代码
2 修改ObjectTranslator.cs 在注入委托时 记录Lua栈信息,在AllDelegateBridgeReleased时打印出来
  1.         Dictionary<int, WeakReference> delegate_bridges = new Dictionary<int, WeakReference>();
  2. #if UNITY_EDITOR
  3.         // 保存weakReference的hash
  4.         private List<int> bridgeLuaRefs = new List<int>();
  5.         // 保存对应的lua stack info
  6.         private List<string> bridgeLuaRefs2 = new List<string>();
  7.         // 开关
  8.         public bool debugDelegateBridgeRelease = false;
  9. #endif
  10.         public object CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx)
  11.         {
  12.             WeakReference weakRef = null;
  13. #if UNITY_EDITOR
  14.             object obj;
  15.             if (this.debugDelegateBridgeRelease)
  16.             {
  17.                 string luaStack = this._GetLuaStack(L);
  18.                 obj = this._CreateDelegateBridge(L, delegateType, idx, out weakRef);
  19.                 if (weakRef != null)
  20.                 {
  21.                     bridgeLuaRefs.Add(weakRef.GetHashCode());
  22.                     bridgeLuaRefs2.Add(luaStack);
  23.                 }
  24.             }
  25.             else
  26.             {
  27.                 obj = this._CreateDelegateBridge(L, delegateType, idx, out weakRef);
  28.             }
  29.             return obj;
  30. #else
  31.             return this._CreateDelegateBridge(L, delegateType, idx, out weakRef);
  32. #endif
  33.         }
  34. #if UNITY_EDITOR
  35.         private string _GetLuaStack(RealStatePtr L)
  36.         {
  37.             var oldTop = LuaAPI.lua_gettop(L);
  38.             // @see https://www.lua.org/pil/25.2.html
  39.             int debug = LuaAPI.xlua_getglobal(L, "debug");
  40.             // LuaAPI.xlua_pushasciistring(L, "getinfo");
  41.             // LuaAPI.xlua_pgettable(L, -2); // 获取getinfo function
  42.             // LuaAPI.xlua_pushinteger(L, 2); // 传入getinfo()第1个参数
  43.             // LuaAPI.xlua_pushasciistring(L, "Sl"); // 传入getinfo()第2个参数
  44.             // var strIndex = LuaAPI.lua_pcall(L, 2, 1, 0); // 执行函数,此时在栈上的结果是一个table
  45.             // // @see https://www.lua.org/pil/25.1.html
  46.             // LuaAPI.lua_pushstring(L, "source"); // 要获取一个table某个key下的值, 要先传入key的值='source'
  47.             // LuaAPI.xlua_pgettable(L, -2); // 获取table[key]返回的值,此时是一个string
  48.             // var luasource = LuaAPI.lua_tostring(L, -1);
  49.             // LuaAPI.lua_pop(L, 1); // 弹出结果
  50.             // LuaAPI.lua_pushstring(L, "currentline");
  51.             // LuaAPI.xlua_pgettable(L, -2);
  52.             // var currentline = LuaAPI.lua_tostring(L, -1);
  53.             // LuaAPI.lua_pop(L, 1);
  54.             // LuaAPI.lua_settop(L, oldTop);
  55.             // string luaStack = luasource + ":" + currentline;
  56.             // 修改为使用traceback()的信息
  57.             LuaAPI.xlua_pushasciistring(L, "traceback");
  58.             LuaAPI.xlua_pgettable(L, -2);
  59.             var strIndex = LuaAPI.lua_pcall(L, 0, 1, 0);
  60.             string luaStack = LuaAPI.lua_tostring(L, -1);
  61.             LuaAPI.lua_pop(L, 1);
  62.             LuaAPI.lua_settop(L, oldTop);
  63.             return luaStack;
  64.         }
  65. #endif
  66.         private object _CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx, out WeakReference weakRef)
  67.         {
  68.             LuaAPI.lua_pushvalue(L, idx);
  69.             LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
  70.             if (!LuaAPI.lua_isnil(L, -1))
  71.             {
  72.                 int referenced = LuaAPI.xlua_tointeger(L, -1);
  73.                 LuaAPI.lua_pop(L, 1);
  74.                 weakRef = delegate_bridges[referenced];
  75.                 if (weakRef.IsAlive)
  76.                 {
  77.                     if (delegateType == null)
  78.                     {
  79.                         return weakRef.Target;
  80.                     }
  81.                     DelegateBridgeBase exist_bridge = weakRef.Target as DelegateBridgeBase;
  82.                     Delegate exist_delegate;
  83.                     if (exist_bridge.TryGetDelegate(delegateType, out exist_delegate))
  84.                     {
  85.                         return exist_delegate;
  86.                     }
  87.                     else
  88.                     {
  89.                         exist_delegate = getDelegate(exist_bridge, delegateType);
  90.                         exist_bridge.AddDelegate(delegateType, exist_delegate);
  91.                         return exist_delegate;
  92.                     }
  93.                 }
  94.             }
  95.             else
  96.             {
  97.                 LuaAPI.lua_pop(L, 1);
  98.             }
  99.             LuaAPI.lua_pushvalue(L, idx);
  100.             int reference = LuaAPI.luaL_ref(L);
  101.             LuaAPI.lua_pushvalue(L, idx);
  102.             LuaAPI.lua_pushnumber(L, reference);
  103.             LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX);
  104.             DelegateBridgeBase bridge;
  105.             try
  106.             {
  107. #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0
  108.                 if (!DelegateBridge.Gen_Flag)
  109.                 {
  110.                     bridge = Activator.CreateInstance(delegate_birdge_type, new object[] { reference, luaEnv }) as DelegateBridgeBase;
  111.                 }
  112.                 else
  113. #endif
  114.                 {
  115.                     bridge = new DelegateBridge(reference, luaEnv);
  116.                 }
  117.             }
  118.             catch (Exception e)
  119.             {
  120.                 LuaAPI.lua_pushvalue(L, idx);
  121.                 LuaAPI.lua_pushnil(L);
  122.                 LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX);
  123.                 LuaAPI.lua_pushnil(L);
  124.                 LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, reference);
  125.                 throw e;
  126.             }
  127.             if (delegateType == null)
  128.             {
  129.                 weakRef = new WeakReference(bridge);
  130.                 delegate_bridges[reference] = weakRef;
  131.                 return bridge;
  132.             }
  133.             try
  134.             {
  135.                 var ret = getDelegate(bridge, delegateType);
  136.                 bridge.AddDelegate(delegateType, ret);
  137.                 weakRef = new WeakReference(bridge);
  138.                 delegate_bridges[reference] = weakRef;
  139.                 return ret;
  140.             }
  141.             catch (Exception e)
  142.             {
  143.                 bridge.Dispose();
  144.                 throw e;
  145.             }
  146.         }
  147.         public bool AllDelegateBridgeReleased()
  148.         {
  149. #if UNITY_EDITOR
  150.             var _bridgeLuaRefs = this.bridgeLuaRefs;
  151.             var _bridgeLuaRefs2 = this.bridgeLuaRefs2;
  152.             this.bridgeLuaRefs = new List<int>();
  153.             this.bridgeLuaRefs2 = new List<string>();
  154. #endif
  155.             bool haveDelegate = false;
  156.             foreach (var kv in delegate_bridges)
  157.             {
  158.                 if (kv.Value.IsAlive)
  159.                 {
  160. #if UNITY_EDITOR
  161.                     if (this.debugDelegateBridgeRelease)
  162.                     {
  163.                         var hash = kv.Value.GetHashCode();
  164.                         for (int i = 0; i < _bridgeLuaRefs.Count; i++)
  165.                         {
  166.                             if (_bridgeLuaRefs[i] == hash)
  167.                             {
  168.                                 UnityEngine.Debug.Log("未释放lua引用: " + _bridgeLuaRefs2.Count + " : " + _bridgeLuaRefs2[i]);
  169.                                 break;
  170.                             }
  171.                         }
  172.                     }
  173. #endif
  174.                     haveDelegate = true;
  175.                 }
  176.             }
  177.             return !haveDelegate;
  178.         }
  179.         public void ReleaseLuaBase(RealStatePtr L, int reference, bool is_delegate)
  180.         {
  181.             if (is_delegate)
  182.             {
  183.                 LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, reference);
  184.                 if (LuaAPI.lua_isnil(L, -1))
  185.                 {
  186.                     LuaAPI.lua_pop(L, 1);
  187.                 }
  188.                 else
  189.                 {
  190.                     LuaAPI.lua_pushvalue(L, -1);
  191.                     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
  192.                     if (LuaAPI.lua_type(L, -1) == LuaTypes.LUA_TNUMBER && LuaAPI.xlua_tointeger(L, -1) == reference) //
  193.                     {
  194.                         //UnityEngine.Debug.LogWarning("release delegate ref = " + luaReference);
  195.                         LuaAPI.lua_pop(L, 1);// pop LUA_REGISTRYINDEX[func]
  196.                         LuaAPI.lua_pushnil(L);
  197.                         LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); // LUA_REGISTRYINDEX[func] = nil
  198.                     }
  199.                     else //another Delegate ref the function before the GC tick
  200.                     {
  201.                         LuaAPI.lua_pop(L, 2); // pop LUA_REGISTRYINDEX[func] & func
  202.                     }
  203.                 }
  204.                 LuaAPI.lua_unref(L, reference);
  205.                 WeakReference weakRef = null;
  206.                 delegate_bridges.TryGetValue(reference, out weakRef);
  207.                 delegate_bridges.Remove(reference);
  208. #if UNITY_EDITOR
  209.                 if (this.debugDelegateBridgeRelease && weakRef != null)
  210.                 {
  211.                     var hash = weakRef.GetHashCode();
  212.                     for (int i = 0; i < this.bridgeLuaRefs.Count; i++)
  213.                     {
  214.                         if (bridgeLuaRefs[i] == hash)
  215.                         {
  216.                             bridgeLuaRefs.RemoveAt(i);
  217.                             bridgeLuaRefs2.RemoveAt(i);
  218.                             break;
  219.                         }
  220.                     }
  221.                 }
  222. #endif
  223.             }
  224.             else
  225.             {
  226.                 LuaAPI.lua_unref(L, reference);
  227.             }
  228.         }
复制代码
在使用var luaEnv = new LuaEnv()的地方设置luaEnv.translator.debugDelegateBridgeRelease = true, 打开开关,当进行luaEnv.Dispose()时,如果lua侧有未释放的引用,则会打印出lua的对应堆栈信息.
所有UI我们使用统一方法来处理清空
其它部分,我们通过打印日志来单独处理 注意自己写的检查方法最初好用,最后 print_func_ref_by_csharp方法好用 可以两个同时使用
参考链接 https://www.jianshu.com/p/58d20d46560a
https://www.cnblogs.com/ghl_carmack/p/7350530.html
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2024-11-24 11:30 , Processed in 0.089879 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

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