Xlua中C#引用Lua变量,导致Lua侧的GC无法回收的原因及解决方法

1. 引用关系导致:

在 XLua 中,当 C# 端引用了 Lua 变量时,Lua 的垃圾回收器(GC)不会回收这些被引用的变量。这是因为 Lua 的 GC 机制是基于引用计数和标记 - 清除算法的。当 C# 端持有对 Lua 变量的引用时,相当于在 Lua 的引用计数系统之外额外增加了一个引用,而 Lua 的 GC 无法感知到这个来自 C# 端的引用。

代码示例:在下面的代码中,luaTable 是 C# 端对 Lua 表 myTable 的引用。只要 luaTable 这个 C# 对象存在,Lua 的 GC 就不会回收 myTable,即使在 Lua 代码中已经没有其他对 myTable 的引用。

cs 复制代码
using XLua;
using UnityEngine;

public class LuaReferenceTest : MonoBehaviour
{
    private LuaEnv luaEnv;
    private LuaTable luaTable;

    void Start()
    {
        luaEnv = new LuaEnv();
        // 执行Lua代码创建一个表
        luaEnv.DoString(@"
            myTable = {1, 2, 3}
        ");
        // 从Lua环境中获取表
        luaTable = luaEnv.Global.Get<LuaTable>("myTable");
    }

    void OnDestroy()
    {
        luaEnv.Dispose();
    }
}
2. 生命周期管理不一致

C# 和 Lua 有各自独立的生命周期管理机制。C# 使用垃圾回收器来管理对象的生命周期,而 Lua 有自己的 GC 机制。当 C# 端引用 Lua 变量时,如果没有正确处理两者的生命周期,就容易导致 Lua 变量无法被回收。例如,在 C# 对象的生命周期结束时,没有及时释放对 Lua 变量的引用,那么 Lua 变量就会一直存在于内存中。

解决方法:

1. 手动释放引用:

在 C# 代码中,当不再需要引用 Lua 变量时,应该手动释放这些引用。对于 LuaTable、LuaFunction 等类型的对象,可以调用 Dispose 方法来释放引用。

代码:

cs 复制代码
using XLua;
using UnityEngine;

public class LuaReferenceTest : MonoBehaviour
{
    private LuaEnv luaEnv;
    private LuaTable luaTable;

    void Start()
    {
        luaEnv = new LuaEnv();
        luaEnv.DoString(@"
            myTable = {1, 2, 3}
        ");
        luaTable = luaEnv.Global.Get<LuaTable>("myTable");
    }

    void OnDestroy()
    {
        // 手动释放Lua表的引用,这样
        // 当 C# 对象销毁时,会释放对 Lua 表的引用,使得 Lua 的 GC 可以回收该表。
        if (luaTable != null)
        {
            luaTable.Dispose();
            luaTable = null;
        }
        luaEnv.Dispose();
    }
}
2. 使用弱引用:

XLua 提供了弱引用的机制,可以让 C# 端以弱引用的方式引用 Lua 变量。当 Lua 变量没有其他强引用时,即使 C# 端有弱引用,Lua 的 GC 也可以回收该变量。

代码:

cs 复制代码
using XLua;
using UnityEngine;

public class LuaWeakReferenceTest : MonoBehaviour
{
    private LuaEnv luaEnv;
    //使用 WeakReference<LuaTable> 来持有对 Lua 表的弱引用。
    //当释放了强引用后,Lua 的 GC 可以正常回收该表。
    private WeakReference<LuaTable> weakTableRef;

    void Start()
    {
        luaEnv = new LuaEnv();
        luaEnv.DoString(@"
            myTable = {1, 2, 3}
        ");
        LuaTable luaTable = luaEnv.Global.Get<LuaTable>("myTable");
        weakTableRef = new WeakReference<LuaTable>(luaTable);
        // 释放强引用
        luaTable.Dispose();
    }

    void OnDestroy()
    {
        luaEnv.Dispose();
    }
}
3. 及时清理 C# 端缓存

如果在 C# 端有缓存 Lua 变量的情况,当缓存不再需要时,应该及时清理这些缓存。例如,在一个静态列表中缓存了多个 Lua 表:

代码:

cs 复制代码
using XLua;
using UnityEngine;
using System.Collections.Generic;

public class LuaCacheTest : MonoBehaviour
{
    private static List<LuaTable> luaTableCache = new List<LuaTable>();
    private LuaEnv luaEnv;

    void Start()
    {
        luaEnv = new LuaEnv();
        luaEnv.DoString(@"
            myTable1 = {1, 2, 3}
            myTable2 = {4, 5, 6}
        ");
        LuaTable table1 = luaEnv.Global.Get<LuaTable>("myTable1");
        LuaTable table2 = luaEnv.Global.Get<LuaTable>("myTable2");
        luaTableCache.Add(table1);
        luaTableCache.Add(table2);
    }

    void OnDestroy()
    {
        // 清理缓存
        foreach (var table in luaTableCache)
        {
            table.Dispose();
        }
        luaTableCache.Clear();
        luaEnv.Dispose();
    }
}
相关推荐
PfCoder2 小时前
C#中定时器之System.Timers.Timer
c#·.net·visual studio·winform
星空露珠6 小时前
速算24点所有题库公式
开发语言·数据库·算法·游戏·lua
人工智能AI技术9 小时前
【C#程序员入门AI】本地大模型落地:用Ollama+C#在本地运行Llama 3/Phi-3,无需云端
人工智能·c#
MyBFuture12 小时前
C#数组详解:一维二维与交错数组
开发语言·windows·c#·visual studio·vision pro
星空露珠13 小时前
速算24点检测生成核心lua
开发语言·数据库·算法·游戏·lua
有来技术13 小时前
ASP.NET Core 权限管理系统(RBAC)设计与实现|vue3-element-admin .NET 后端
vue.js·后端·c#·asp.net·.net
张人玉15 小时前
C#WinFrom中show和ShowDialog的区别
开发语言·microsoft·c#
m0_7482331715 小时前
C#:微软的现代编程利器
开发语言·microsoft·c#
Traced back15 小时前
SQL Server数据自动清理系统最终版(C# WinForms完整源码)
数据库·c#·.net