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();
    }
}
相关推荐
闪电麦坤954 小时前
C#:base 关键字
开发语言·c#
mingupup5 小时前
C#连接小智服务器并将音频解码播放过程记录
c#
爱吃奶酪的松鼠丶8 小时前
.net GRPC服务搭建,跨进程调用。C#应用和Python应用之间的通信。
python·c#·.net
勘察加熊人13 小时前
forms实现俄罗斯方块
c#
艾妮艾妮17 小时前
C语言常见3种排序
java·c语言·开发语言·c++·算法·c#·排序算法
小码编匠17 小时前
.NET 验证码生成神器基于 SkiaSharp 的高性能方案
后端·c#·.net
专注VB编程开发20年17 小时前
Aspose.words,Aspose.cells,vb.net,c#加载许可证,生成操作选择:嵌入的资源
c#·word·.net·vb.net
andy552018 小时前
.NET 使用 WMQ 连接Queue 发送 message 实例
xml·c#·wmq·c# 连接wmq·发送消息到wmq
破罐子不摔18 小时前
【C#使用S7.NET库读取和写入西门子PLC变量】
java·c#·.net
杰尼杰尼丶18 小时前
Winform MQTT客户端连接方式
c#·winform