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

【ToLua】源码学习总结

[复制链接]
发表于 2021-8-7 22:21 | 显示全部楼层 |阅读模式
前言

在介绍 ToLua 前,我们首先来了解一下在游戏开发中,热更新的概念。
热更新
热更新是一种手游及App常用的更新方式,举例来说,游戏上线后,玩家需要通过应用商店及其他渠道下载第一个版本。在运营的过程中,如游戏需要更换UI、修改逻辑、开放功能等,此时若不使用热更新技术,就需要重新打包,那么玩家也就需要通过应用商店或其他渠道重新下载游戏。 热更新可以在不重新下载客户端的情况下,更新游戏的内容。
然而c#是一门编译型语言,其运行之前需要进行编译,而编译的过程在移动平台无法完成,所以当我们游戏的逻辑更改,代码发生变化时,我们就需要重新在开发环境下编译,然后重新打包,让玩家下载最新版本。这个过程中,会下载很多不需要更新的资源,便会增加玩家的时间及流量消耗,造成不好的用户体验。因此在移动平台中便就出现了热更新技术。
现在主流的热更新方式主要有LuaILRuntime
在unity的lua热更新中,有ulua、slua、xlua、tolua等多种热更新方案,这些方案提供了C#与Lua的互相调用机制。而我们项目用的是tolua,今天我们就来学习一下tolua的源码。
Tolua

Tolua是Unity静态绑定lua的一个解决方案,它通过C#中集成lua的插件,可以自动生成用于在lua中访问Unity的绑定代码,并把C#中的常量、变量、函数、属性、类以及枚举暴露给lua。其从cstolua衍变而来。
在学习之前,先明确一下我们想学到的一些知识点:
目录

    wrap文件是如何生成,为什么要生成wrap?lua是怎么获取、调用c#的静态方法、成员方法?c#对象在lua栈里是以什么形式存在的?c#如何调用到lua的方法的?tolua是怎么把lua的table、function转成c#的table、function实例的?tolua把对象存在objects里,而值类型的Struct如果存在objects了,会发生封箱、拆箱的操作,tolua是如何避免的?objects里的对象是什么时候会被移除?lua怎样才算正确释放了c#对象?利用tolua如何实现热更?针对lua和c#的交互有什么优化手段?
细节

    wrap文件是如何生成,为什么要生成wrap?
每个wrap文件都是对一个c#类的包装,在lua中,通过对wrap类中的函数调用,间接的对c#实例进行操作。
tolua之wrap文件的原理与使用 - blueberryzzz - 博客园


2. lua是怎么获取、调用c#的静态方法、成员方法?c#对象在lua栈里是以什么形式存在的?
【unity游戏开发】tolua学习-C#和Lua的交互细节
c#对象在lua栈里是以数组下标存储的,需要调用的时候,可以通过这个索引,通过ObjectTranslator.GetObject(int udata)访问到C#层对象
3. c#如何调用到lua的方法的?tolua是怎么把lua的table、function转成c#的table、function实例的?
【unity游戏开发】tolua学习-C#和Lua的交互细节


4. tolua把对象存在objects里,而值类型的Struct如果存在objects了,会发生封箱、拆箱的操作,tolua是如何避免的?
Unity中xLua与toLua对Vector3的优化


5. objects里的对象是什么时候会被移除?lua怎样才算正确释放了c#对象?
Lua和C#对象释放图文详解:用Unity+Lua开发游戏,有什么好的办法进行性能检测?

①c#内存泄露
c# object返回给lua,是通过dictionary将lua的userdata和c# object关联起来,只要lua中的userdata没回收,c# object也就会被这个dictionary拿着引用,导致无法回收。
最常见的就是gameobject和component,如果lua里头引用了他们,即使你进行了Destroy,也会发现他们还残留在mono堆里。
不过,因为这个dictionary是lua跟c#的唯一关联,所以要发现这个问题也并不难,遍历一下这个dictionary就很容易发现。ulua和tolua下这个dictionary在ObjectTranslator类、slua则在ObjectCache类。
那么,lua是什么时候移除了对c#对象的引用的?
tolua在Update里调用了luaState.Collect(); 然后调用了ObjectTranslator.Collect();用了脏标记模式,gcList长度大于0的时候进行回收,调用DestroyUnityObject()
//ObjectTranslator.cs
        //延迟删除处理
        void DestroyUnityObject(int udata, UnityEngine.Object obj)
        {
            object o = objects.TryGetValue(udata);

            if (object.ReferenceEquals(o, obj))
            {
                RemoveObject(o, udata);
                //一定不能Remove, 因为GC还可能再来一次
                objects.Destroy(udata);     

                if (LogGC)
                {
                    Debugger.Log("destroy object {0}, id {1}", o, udata);
                }
            }

            UnityEngine.Object.Destroy(obj);
        }


Lua GC
Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:
    collectgarbage("collect"): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:collectgarbage("count"): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。collectgarbage("restart"): 重启垃圾收集器的自动运行。collectgarbage("setpause"): 将 arg 设为收集器的 间歇率。 返回 间歇率 的前一个值。collectgarbage("setstepmul"): 返回 步进倍率 的前一个值。collectgarbage("step"): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。collectgarbage("stop"): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。
如何监测Lua的编程产生内存泄露:  
方法:①:
1. 针对会产生泄露的函数,先调用collectgarbage("collect")和collectgarbage("count"),取得最初的内存使用情况。
2. 函数调用后, collectgarbage("collect")进行收集, 并使用collectgarbage("count")再取得当前内存, 最后记录两次的使用差。
3.可以保存函数调用前后的_G到本地文件,然后使用软件比较前后两次的_G的内容差,可以获取到泄漏的具体内容。
方法:②:游戏开发:Unity中Lua造成的堆内存泄露问题


6. 利用tolua如何实现热更?
编辑器运行时热更:【Unity游戏开发】Lua更新运行时代码
线上包热更:【unity游戏开发】AB学习(三)—热更学习


7. 针对lua和c#的交互有什么优化手段?
Lua配置表导出优化
Lua配置表导入优化:延迟导入
参考:

朔宇:Unity3D热更新技术点——ToLua(上)
Xlua源码学习
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2024-11-24 03:16 , Processed in 0.091593 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

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