该错误主要是在调用LuaEnv.Dispose()时引起,一般用于游戏重启时将整个lua虚拟机重置.
报错的主要原因有:
- 在同一个方法中释放了delegate的引用,但又立即调用了LuaEnv.Dispose().
- 没有在C#侧释放标记有[CSharpCallLua]的delegate(赋值为null).
详细说明可以在xLua自带的FAQ(xLua/Doc/faq.md)中找到.
-
对于第1个问题, 可以将LuaEnv.Dispose()的释放代码单独放到另一个方法中, 参考 https://github.com/Tencent/xLua/issues/634
-
对于第2个问题:
xlua提供了一个lua方法(该方法位于XLua/Resources/xlua/util.lua.txt中): print_func_ref_by_csharp()来打印所有通过delegate注入到C#中的lua方法. 通过得到的信息可以依次检查lua往C#中的哪些地方注入了方法,这样就在C#中去把那些delegate类型的变量赋值为null进行释放了.
但依然难免出现疏忽的情况,且当大量使用C#调用lua这种逻辑时,排查问题会更麻烦,所以如果想在报错时确切的知道到底是lua的哪个调用处没有释放,就需要对xLua源码进行额外的处理(v2.1.13测试没问题).
报错的地方是在LuaEnv.cs的Dispose(bool dispose)方法中抛出的, 如下图:
image.png
该处又是调用了translator.AllDelegateBridgeReleased()来得到的检查结果, translator是ObjectTranslator.cs
在生成的各种wrap文件中,对于delegate的获取处理有如下类似代码:
1 2 3 | ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L); XXClass gen_to_be_invoked = (XXClass)translator.FastGetCSObj(L, 1); gen_to_be_invoked.DelegateHandler = translator.GetDelegate<XXClass.DelegateType>(L, 2); |
translator依然是ObjectTranslator,其GetDelegate()定义如下图:
image.png
对于delegate类型的lua传入肯定是个function,所以一定会执行到红圈处的代码,即CreateDelegateBridge().
修改逻辑如下:
- 在CreateDelegateBridge()时获取delegate的包装对象WeakReference; 同时获取lua处的调用堆栈信息,即通过lua的debug.getinfo()来得到lua传入function到C#时的文件和代码行号. 将这2个信息保存起来.
- 在ReleaseLuaBase()中移除保存的信息,该方法被LuaEnv.Tick()调用.
- 在AllDelegateBridgeReleased()中,当检查到某个delegate没有释放时,就打印其WeakReference(在第1步中)关联保存的lua信息,即可精确得到具体是哪里没有释放.
具体修改代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | Dictionary<int, WeakReference> delegate_bridges = new Dictionary<int, WeakReference>(); #if UNITY_EDITOR // 保存weakReference的hash private List<int> bridgeLuaRefs = new List<int>(); // 保存对应的lua stack info private List<string> bridgeLuaRefs2 = new List<string>(); // 开关 public bool debugDelegateBridgeRelease = false; #endif public object CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx) { WeakReference weakRef = null; #if UNITY_EDITOR object obj; if (this.debugDelegateBridgeRelease) { string luaStack = this._GetLuaStack(L); obj = this._CreateDelegateBridge(L, delegateType, idx, out weakRef); if (weakRef != null) { bridgeLuaRefs.Add(weakRef.GetHashCode()); bridgeLuaRefs2.Add(luaStack); } } else { obj = this._CreateDelegateBridge(L, delegateType, idx, out weakRef); } return obj; #else return this._CreateDelegateBridge(L, delegateType, idx, out weakRef); #endif } #if UNITY_EDITOR private string _GetLuaStack(RealStatePtr L) { var oldTop = LuaAPI.lua_gettop(L); // @see https://www.lua.org/pil/25.2.html int debug = LuaAPI.xlua_getglobal(L, "debug"); LuaAPI.xlua_pushasciistring(L, "getinfo"); LuaAPI.xlua_pgettable(L, -2); // 获取getinfo function LuaAPI.xlua_pushinteger(L, 2); // 传入getinfo()第1个参数 LuaAPI.xlua_pushasciistring(L, "Sl"); // 传入getinfo()第2个参数 var strIndex = LuaAPI.lua_pcall(L, 2, 1, 0); // 此时在栈上的结果是一个table // @see https://www.lua.org/pil/25.1.html LuaAPI.lua_pushstring(L, "source"); // 要获取一个table某个key下的值, 要先传入key的值='source' LuaAPI.xlua_pgettable(L, -2); // 获取table[key]返回的值,此时是一个string var luasource = LuaAPI.lua_tostring(L, -1); LuaAPI.lua_pop(L, 1); // 弹出结果 LuaAPI.lua_pushstring(L, "currentline"); LuaAPI.xlua_pgettable(L, -2); var currentline = LuaAPI.lua_tostring(L, -1); LuaAPI.lua_pop(L, 1); LuaAPI.lua_settop(L, oldTop); string luaStack = luasource + ":" + currentline; return luaStack; } #endif private object _CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx, out WeakReference weakRef) { LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); if (!LuaAPI.lua_isnil(L, -1)) { int referenced = LuaAPI.xlua_tointeger(L, -1); LuaAPI.lua_pop(L, 1); weakRef = delegate_bridges[referenced]; if (weakRef.IsAlive) { if (delegateType == null) { return weakRef.Target; } DelegateBridgeBase exist_bridge = weakRef.Target as DelegateBridgeBase; Delegate exist_delegate; if (exist_bridge.TryGetDelegate(delegateType, out exist_delegate)) { return exist_delegate; } else { exist_delegate = getDelegate(exist_bridge, delegateType); exist_bridge.AddDelegate(delegateType, exist_delegate); return exist_delegate; } } } else { LuaAPI.lua_pop(L, 1); } LuaAPI.lua_pushvalue(L, idx); int reference = LuaAPI.luaL_ref(L); LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_pushnumber(L, reference); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); DelegateBridgeBase bridge; try { #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 if (!DelegateBridge.Gen_Flag) { bridge = Activator.CreateInstance(delegate_birdge_type, new object[] { reference, luaEnv }) as DelegateBridgeBase; } else #endif { bridge = new DelegateBridge(reference, luaEnv); } } catch (Exception e) { LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_pushnil(L); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_pushnil(L); LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, reference); throw e; } if (delegateType == null) { weakRef = new WeakReference(bridge); delegate_bridges[reference] = weakRef; return bridge; } try { var ret = getDelegate(bridge, delegateType); bridge.AddDelegate(delegateType, ret); weakRef = new WeakReference(bridge); delegate_bridges[reference] = weakRef; return ret; } catch (Exception e) { bridge.Dispose(); throw e; } } public bool AllDelegateBridgeReleased() { var _bridgeLuaRefs = this.bridgeLuaRefs; var _bridgeLuaRefs2 = this.bridgeLuaRefs2; this.bridgeLuaRefs = new List<int>(); this.bridgeLuaRefs2 = new List<string>(); foreach (var kv in delegate_bridges) { if (kv.Value.IsAlive) { #if UNITY_EDITOR if (this.debugDelegateBridgeRelease) { var hash = kv.Value.GetHashCode(); for (int i = 0; i < _bridgeLuaRefs.Count; i++) { if (_bridgeLuaRefs[i] == hash) { UnityEngine.Debug.Log("未释放lua引用: " + _bridgeLuaRefs2.Count + " : " + _bridgeLuaRefs2[i]); break; } } } #endif return false; } } return true; } public void ReleaseLuaBase(RealStatePtr L, int reference, bool is_delegate) { if (is_delegate) { LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, reference); if (LuaAPI.lua_isnil(L, -1)) { LuaAPI.lua_pop(L, 1); } else { LuaAPI.lua_pushvalue(L, -1); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); if (LuaAPI.lua_type(L, -1) == LuaTypes.LUA_TNUMBER && LuaAPI.xlua_tointeger(L, -1) == reference) // { //UnityEngine.Debug.LogWarning("release delegate ref = " + luaReference); LuaAPI.lua_pop(L, 1);// pop LUA_REGISTRYINDEX[func] LuaAPI.lua_pushnil(L); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); // LUA_REGISTRYINDEX[func] = nil } else //another Delegate ref the function before the GC tick { LuaAPI.lua_pop(L, 2); // pop LUA_REGISTRYINDEX[func] & func } } LuaAPI.lua_unref(L, reference); WeakReference weakRef = null; delegate_bridges.TryGetValue(reference, out weakRef); delegate_bridges.Remove(reference); #if UNITY_EDITOR if (this.debugDelegateBridgeRelease && weakRef != null) { var hash = weakRef.GetHashCode(); for (int i = 0; i < this.bridgeLuaRefs.Count; i++) { if (bridgeLuaRefs[i] == hash) { bridgeLuaRefs.RemoveAt(i); bridgeLuaRefs2.RemoveAt(i); break; } } } #endif } else { LuaAPI.lua_unref(L, reference); } } |
在使用new LuaEnv()的地方设置luaEnv.translator.debugDelegateBridgeRelease = true.
转载请注明出处: https://www.jianshu.com/p/58d20d46560a