通过对比:在创建不同字符串时,不论长短字符串,都会有一份不同字符串的拷贝,所以都需要消耗大量内存。1052K VS 1145K
问题一:在对比情景2和情景4时,长字符串都是一份拷贝,为什么内存消耗差异较大?(791K vs 1145K)
答:这是因为使用连字符 .. 时,情景4里面的变量i转为字符串,相当于生成了10000份不同的短字符串拷贝,导致多消耗了300K左右内存,所以在开发中可以适当避免 .. 产生大量字符串的产生。可以考虑使用string.format来格式化字符串。具体效果如下:
Lua5.3.4
collectgarbage("stop"); --close auto gc
collectgarbage("collect") --full gc
for i = 1, 10000 do
local string = string.format("100000000000000000000000000000000000000000%d", i)
end
local after = collectgarbage("count");
print("mem cost:" .. (after - before) .. "K" )
mem cost:799.7K (结果比较接近情景2)
2.1.2 预估字符串消耗
问题二:以情景2为例,最终内存消耗771.48K,这个可以在开发前估算吗?
答:string实际是GCObject对象,所以有些公共的字段。以”Lua”这个字符串为例,TString的内存结构:
在情景2中,实际字符串长度为54。那么内存占用 = sizeof(TString) + 1 + 实际长度 = 25 + 实际长度 = 25 + 54 = 79。理论内存:10000 * 79/ 1024 = 771.48K, 理论跟实际消耗值几乎一致。
2.1.3 与lua5.1.4的对比但由于我们之前也用过lua5.1.4,这个版本所有字符串均引用StringTable一份数据拷贝,理论上不论长短字符串,只要相同的数据只会有一份拷贝。(由于lua5.1.4版本在调用collectgarbage("collect")进行一次完整gc后,luaC_fullgc会调用setthreshold打开自动gc,这里容易踩坑,上述代码运行发现内存消耗明显低于lua5.3版本,非常困惑。所以修改代码在collectgarbage("collect")后,重新调用collectgarbage("stop")关闭自动gc)。修改后在lua5.1.4下面执行对比如下:
Lua5.3.4内存消耗
Lua5.1.4内存消耗
情景1:
相同短字符串
0.580078125K
0.048828125K
情景2:
相同长字符串
771.48K
0.0966796875K
情景3:
不同短字符串
1052.62109375K
1052.62109375K
情景4:
不同长字符串
1145.82421875K
1305.828125K