最大差别在于相同长字符串创建的内存消耗,Lua5.1.4由于统一用stringtable保存一份拷贝,所以内存消耗可以忽略不记,但Lua5.3.4由于会产生不同副本拷贝,消耗明显。
对于短字符串,只会在stringtable中存在一份拷贝,对内存没有什么影响。对于长字符串,需要分两种场合看:
for i =1, 1000 do
Tab[i] = {ccccccccccccccccccccccccccccccccccccccc=i}
end
或者
Tab[1] = {ccccccccccccccccccccccccccccccccccccccc=1}
Tab[2] = {ccccccccccccccccccccccccccccccccccccccc=2}
Tab[3] = {ccccccccccccccccccccccccccccccccccccccc=3}
...
Tab[100] = {ccccccccccccccccccccccccccccccccccccccc=100}
对于这两种情况,由于lua程序进行代码文件词法分析时,第一种调用一次luaS_newlstr创建”ccccccccccccccccccccccccccccccccccccccc”, 而第二种会调用100次。正如我们前面的分析,长字符串每次调用都会生成一份新的拷贝,所以第二种情况会有N份的字符串内存消耗。
我们服务器的表资源,转表后都是按第二种形态存在,所以一定要切记key的字符串长度不要超过40,否则产生无谓的内存浪费和大量的GC对象,影响GC效率。
2.2 table类型在开发过程中,table是最常见的数据结构,每条记录对外都是key-value的方式来读写。他的底层是用array + hashtable的方式管理数据的,但对外是透明的。不论array还是hashtable都是连续的内存分布。在查找时:
1. 如果key是整型, 并且 key > 1 and key < max_array_size, 直接取array[key]数据
2. 其他情况,默认读取hashtable。Hashtable的管理方有些特别,当不同key hash到同一个node时,用链表来维护这些冲突节点。与stringtable 链表节点动态分配的方法不同, HashTable使用空闲链表来维护冲突节点。
2.2.1 Array or HashTable?
首先说一种典型的情况,调用table.insert 或者table[#table + 1] 按序插入列表的,就是存放在array里面。
2.2.2 rehash动态扩容原理
在插入新节点时,如果通过freelist找不到可用节点就触发rehash。在新创建table时,node节点数量为0,当插入第1条数据时触发内存分配2^0=1个内存节点。当插入第2条记录时,因为节点都已经使用,所以又触发rehash,分配2^1=2个内存节点。依次类推,在插入第3、5、9时,触发分配一个更大的内存,可以简单理解成重新分配一块oldsize * 2 的内存,把原有的hashTable的数据重新hash插入到新的内存中,再释放掉原有的内存。综上,每次内存的增长都是按2的指数倍来增长。
1、Rehash导致的cpu消耗