Lua的内存监测和回收

Lua内存是自动收集的, 这点跟Java类似, 不被任何对象或全局变量引用的数据,将被首先标记为回收,不需要开发者做任何事情.但是,正如Java也会有内存泄露一样, Lua也会有, 只不过,跟C++的不同,它是由于代码执行所装载的资源,并没有被彻底销毁而导致,其中,最臭名昭著的就是不小心把局部变量声明成了全局变量(忘了加local修饰符)。 类似这样造成的内存泄露, 跟任何其他语言的内存泄露一样,容易产生,却难以察觉, 给开发的应用带来潜在的很大隐患.

 

那么, 有没有一些有效的解决办法, 来解决这个这个隐患呢, 答案就是collectgarbage. collectgarbage就是开放给Lua开发人员, 用于监听Lua的内存使用情况(collectgarbage("count")), 同时,它还提供了collectgarbage("collect"),允许在适当的时候进行显式的回收.

现在,通过测试代码来看看,如何玩转collectgarbage.

首先,为了有明显的对比, 先来看没有产生泄露的情况, 运行以下的test1(代码如下):


 

运行结果如下:



 这里看到, 被local 声明的colen加了5000数组, test1调用后, 内存增加了大概300K(25906K-25620K).
现在,我们来做内存回收(调用mem函数, 代码如下):


 运行结果:

( 为了保证内存的稳定,以上注意mem被调用了多次, 再第2次, 可以看到内存开始下降, 最后,大概在25618K稳定下来)
 好了,  从最初的 25620K,  到回收后的 25618K,  两者并没有发生变化(还少了2K,嘿嘿, 这应该是误差了), 也就是说,函数test1的执行,并没有产生无法回收的内存,没有泄露出现.

好了,现在运行有泄露的test2(代码如下), test2跟test1相比,只有一处不同:就是colen被误声明为全局:


结果:

 也就是说,内存也在25906K,跟test1几乎是相等, 好了,现在再调用回收(mem)函数,产生结果如下

 为了保证函数回收被执行,这次,总共调用了7次mem函数(看以上打印行数), 那么,从上面的结果我们看, 很不幸, 从第1次,到最后第7次, 内存都还是稳定在25905K左右, 也就是说, 跟调用test2前相比,即使Lua进行了内存回收, 内存却不会将下来  看来 300K(25906K-25620K) 内存 由于已放到了全局函数中,是永远没有机会被回收到了!

总结一: 如何监测Lua的编程产生内存泄露:

1.       针对会产生泄露的函数,先调用collectgarbage("count"),取得最初的内存使用

2.       函数调用后, collectgarbage("collect")进行收集, 并使用collectgarbage("count")再取得当前内存, 最后记录两次的使用差

3.       从test1的收集可看到, collectgarbage("collect")被调用,并不保证一次成功, 所以, 大可以调用多次

总结二: 如何避免Lua应用中出现的内存使用过大行为:

1.       当然是代码实现不出现泄露, (废话*&%$()

2.       在测试中,其实还发现, Lua中被分配的内存,其实并不会自动回收(个人估计要么就是Lua虚拟机没有做这个事情,要么就是回收的时机是在C层), 所以, 为了避免内存过大, 应用的运行时,可能需要定期的(调用collectgarbage("collect"),又或者collectgarbage("step"))进行显式回收。

  • 大小: 2.7 KB
  • 大小: 6.3 KB
  • 大小: 4.6 KB
  • 大小: 5.7 KB
  • 大小: 9.2 KB
  • 大小: 2.3 KB
  • 大小: 2.3 KB
### Lua 内存管理机制 Lua 使用自动垃圾回收(Garbage Collection, GC)机制来管理内存,开发者无需手动释放不再使用的对象。其核心机制基于“标记-清除”(Mark-Sweep)算法,在每次完整的 GC 周期中,Lua 会从根对象出发,递归地遍历所有可达的对象,并将它们标记为“存活”。未被标记的对象被视为不可达,将在清除阶段被释放[^4]。 在 Lua 5.1 中,GC 是全量的标记-清除机制,而在 Lua 5.2 及之后版本中引入了**分代收集**(Generational GC),以提高效率。分代收集的核心思想是:新创建的对象更可能很快死亡,而已经存活一段时间的对象则很可能继续存活,因此可以分别处理不同“年龄”的对象,从而减少每次 GC 的工作量[^3]。 Lua内存管理器还支持自定义分配函数,允许嵌入式系统或游戏引擎对内存分配进行细粒度控制。例如 Unity 引擎中集成了 Lua,但其自身的内存检查工具无法覆盖 Lua 虚拟机部分,因此需要额外的工具辅助分析[^3]。 ### 排查内存泄漏 尽管 Lua 具有自动垃圾回收机制,但由于引用关系的存在,仍然可能出现“逻辑上的内存泄漏”,即对象本应被释放,但由于某些引用未被清除,导致 GC 无法回收。这类问题通常表现为内存持续增长,尤其是在长时间运行的服务端程序或游戏中[^4]。 排查 Lua 内存泄漏的关键在于以下几点: - **识别无用但仍被引用的对象**:使用 `debug` 库中的 `getregistry` `getmetatable` 等函数可以帮助查看全局注册表元表,这些地方常常是隐藏引用链的源头。 - **利用 lua-snapshot 工具**:该工具能够捕获当前 Lua 状态的完整内存布局,通过比较不同时间点的快照,可以快速定位内存增长点。它适用于 Linux、Windows macOS 多平台,并提供示例代码帮助开发者快速上手[^1]。 - **减少不必要的对象创建**:频繁创建临时对象会增加 GC 的负担,进而影响性能。可以通过复用对象、避免在循环中创建对象等方式优化代码。 - **监控 GC 行为**:Lua 提供了 `collectgarbage("count")` 来获取当前堆内存大小,也可以通过设置 `setpause` `setstepmul` 参数调整 GC 的行为,使其更适应特定应用的需求。 ### 示例代码:监控内存使用情况 ```lua -- 获取当前 Lua内存大小(单位为 KB) local memory_kb = collectgarbage("count") print(string.format("Current memory usage: %.2f KB", memory_kb)) ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值