先看一个例子
1. 实际例子
- 先造一个100M的文件,文件名为:test-100M。
dd if=/dev/urandom of=test1 bs=1M count=10
- 写一段lua程序读取这个文件。命名为test.lua
local function load_file(path)
if not path then
return nil
end
-- 以只读方式打开文件
local file = io.open(path, "r")
local content = file:read("*all")
io.close(file)
end
load_file("test-100M")
while (true)
do
end
- 使用ps aux 名检测这个进程的内存变化情况。
while true; do ps aux | grep test.lua; sleep 1; done
可以看到test.lua进程物理内存消耗了399468kb = 390M
显然在实际场景中,这样的内存消耗是不能接受的,更要命的是在 load_file调用完成后,内存一直没有降下来。
2. 优化过程
通过刚才例子可以看出,test.lua存在两个问题
1). 内存消耗过大;
2). 内存消耗以后一直没有释放。
解决第一个问题思路是重写lua read函数,自己用c实现。
今天重点讨论第二个,怎么在高内存消耗以后,及时的释放掉内存?
- 主动调用lua垃圾回收函数
通过调用collectgarbage("collect") 显示的去回收内存。 这样做是可以释放掉一部分内存,但由于lua虚拟机出于对内存效率的需要,不会将所有内存都还给操作系统。具体情况可以自己实验下。
- 主动调用lua垃圾回收函数
- 将内存消耗过大的操作单独起系统命令执行,设置超时时间,执行完成,进程退出,由操作系统回收这块内存。
- 如果lua进程调用了so库或者其他语言的库,那么首先要检测是否有内存泄露导致内存没有及时释放。
- 如果调用栈中push的操作过多,会导致,栈空间变大,短时间内无法释放,可以更改数据格式,采用更加节省内存的protobuf格式。
- 其它,如果消耗内存的操作不是读文件,而是像字符串拼接、table。那么可以使用table弱表。 字符串拼接可以用table.concat提高内存回收效率。
2. 优化成果展示
优化后的源码(这里主要展示方式1):
local function load_file(path)
if not path then
return nil
end
-- 以只读方式打开文件
local file = io.open(path, "r")
local content = file:read("*all")
io.close(file)
end
load_file("test-100M")
while (true)
do
end
local mem1 = collectgarbage("count")
print("初始内存:", mem1, "kb")
load_file()
local mem3 = collectgarbage("count")
print("\n垃圾收集前:", mem3, "kb")
collectgarbage("collect")
collectgarbage("collect")
collectgarbage("collect")
local mem4 = collectgarbage("count")
print("\n垃圾收集后:", mem4, "kb")
while (true)
do
end
内存使用统计: