解决Unity+XLua内存的一些记录

2024/03 31 09:03

一、跟踪对象引用

Lua中的对象被持有(引用),会导致资源的引用也无法断开,所以需要追踪lua中对象是否存在。

使用lua的weak表,把需要追踪的对象放在weak表中,在指定的时候检查此表中是否存在对象,若未被回收,则说明出现了泄漏。

local _MV = { __mode = "v" }

---@class WeakObjMgr
local _M = {}
_M.__index = _M

---@param obj table|function 要追踪的对象
---@param typeName string 大类名称
---@param objName string 小类名称
function _M:Add(typeName, objName, obj)
    local objType = type(obj)
    if objType ~= "function" and objType ~= "table" then
        return false
    end
    local pool = self.list[typeName]
    if pool == nil then
        pool = {}
        self.list[typeName] = pool
    end
    table.insert(pool, setmetatable({ objName, obj }, _MV))
    return true
end

--- 汇报
function _M:Report()
    collectgarbage("collect")
    local tips = {}
    for typeName, pool in pairs(self.list) do
        local len = #pool
        for i = len, 1, -1 do
            if pool[i][2] == nil then
                table.remove(pool, i)
            else
                table.insert(tips, typeName .. ":" .. tostring(pool[i][1]))
            end
        end
    end
    return table.concat(tips, "\n")
end

---@type WeakObjMgr
WeakObjMgrIns = setmetatable({ list = {} }, _M)

--- 使用的时候WeakObjMgrIns:Add("mainCity", "avatar", avatarObj)

二、在XLua中添加新增userdata的追踪

public partial class ObjectPool
{
    public int GetCount()
    {
        int c = 0;
        for(int i=0; i<list.Length; ++i)
        {
            if (list[i].obj != null)
            {
                ++c;
            }
        }
        return c;
    }

    private HashSet<int> mSaveSnap = new HashSet<int>();
    public void SaveSnap()
    {
        for(int i=0; i<list.Length; ++i)
        {
            var obj = list[i].obj;
            if (obj != null)
            {
                mSaveSnap.Add(obj.GetHashCode());
            }
        }
    }

    private static string GetObjName(object obj)
    {
        try
        {
            if (obj is UnityEngine.GameObject)
            {
                return (obj as UnityEngine.GameObject).name;
            }

            ...其它的一些对象名称
        }
        catch(Exception)
        {
        }
        return string.Empty;
    }

    // 对比出增量
    public string Compare()
    {
        HashSet<int> current = new HashSet<int>();

        for(int i=0; i<list.Length; ++i)
        {
            var obj = list[i].obj;
            if (obj != null)
            {
                current.Add(obj.GetHashCode());
            }
        }

        List<(string typeName, string objName, int n)> ls = new List<(string typeName, string objName, int)>();
        foreach(var id in current)
        {
            if (mSaveSnap.Contains(id))
            {
                continue;
            }

            if (ObjectPool.mAllObjs.TryGetValue(id, out var info))
            {
                ls.Add((info.tyepName, info.objName, 1));
            }
        }

        ls.Sort( (a,b)=>
        {
            if (a.typeName == b.typeName)
            {
                return a.objName.CompareTo(b.objName);
            }
            return a.typeName.CompareTo(b.typeName);
        });

        int count = ls.Count;
        for(int i=count-1; i>0; --i)
        {
            if (ls[i].typeName == ls[i-1].typeName && ls[i].objName == ls[i-1].objName)
            {
                ls.RemoveAt(i);
                ls[i-1] = (ls[i-1].typeName, ls[i-1].objName, ls[i-1].n+1);
            }
        }

        StringBuilder sb = new StringBuilder();

        foreach(var s in ls)
        {
            if (s.n > 1)
                sb.AppendLine($"{s.typeName} [{s.n}] : {s.objName}");
            else
                sb.AppendLine($"{s.typeName} : {s.objName}");
        }
        return sb.ToString();
    }
    
    public static Dictionary<int, (string tyepName, string objName)> mAllObjs = new Dictionary<int, (string, string)>();
    
    public void PostAdd(object obj)
    {
        if (obj == null)
        {
            return;
        }
    
        var typeName = obj.GetType().Name;
        var objName = GetObjName(obj);
        var hashCode = obj.GetHashCode();
        mAllObjs[hashCode] = (typeName, objName);
    }
}

然后定时进行Compare

三、XLua送的LuaMemoryLeakChecker

不太好用,输出出来的对象比较混乱

未完待续