lua gc和weaktable

来源:互联网 发布:连云港联通网络电视 编辑:程序博客网 时间:2024/06/05 10:59

如果你对lus语言的中weak table不明白的话,那这篇文章应该对你有帮助。所有脚本语言几乎都有垃圾回收器(GC),当然lua也有。不明白GC不要紧,下面让我来解释:这一切要先从现代编程语言的鼻祖 — C语言开始。如果你在C中要使用变量,可以这样定义变量,如:

[cpp] view plain copy
  1. void hello()  
  2. {int a = 0; // 局部变量声明  
  3. }  

局部变量a的生与死是已经确定的,就是{括号和}括号之间。

这样会很安全,因为函数在执行完后会清栈(清理变量),所以你不用去考虑变量的生与死,但,它不够灵活,因为它的作用域被界限了,它只能在这个函数里被使用。

换种手段,定义全局变量,如下:
[cpp] view plain copy
  1. static int a = 0;// 全局变量声明  
  2. void hello1()  
  3. {  
  4. a = 1;  
  5. }  
  6. void hello2()  
  7. {  
  8. a = 2;  
  9. }  

很显然,这样很灵活,到哪里都可以使用。不过自从它生之后,就不会结束了,直到程序关闭时它才结束。这意味着,程序运行期间无论如何它都将存在。你面对的将是一个永恒的变量,如果你定义了10,100,1000个...那将会是一堆...(僵尸?)
这样都很不好,你无法控制变量的生与死,无法达到灵活处理变量生与死的目的。。想亲手控制变量的生与死?C便提供了一个函数实现你的梦想 - malloc,如下:

[cpp] view plain copy
  1. int* p = (int *)malloc(sizeof(int)); // 手动创建  


这样p便存储了申请过来的这块内存首地址,现在就可以使用了,如下:

[cpp] view plain copy
  1. *p = 1;  

当你不再使用这块内存的时候,那你就使用free函数释放(清理)它,否则它将一直存在。

[cpp] view plain copy
  1. free(p);   // 手动释放   
如果你不这样做,那内存将会增长直到程序崩溃(世界就乱了)。这样子看,第3个方法很好很灵活,但是一旦你定义了这种变量多了,你将成为上帝 — 你必须管理每个变量的生与死,哪是1,10,100,1000。一个都不能漏,否则世界的平衡就乱了(有人不死啦),换句话你程序也将挂了。

这样看来很显然不是每个人都喜欢或者能做上帝,在这样的情况下,有一个东西横空出世了 — GC!它就是这个问题的救世主,下面详细介绍:


GC - Garbage Collectoion - 垃圾回收器
它的作用就是变量你随意定义,手尾我来跟,你只须定义变量的生,变量的死由我来负责。那你不经地问:那你怎么知道这个变量我不用了?那这个问题就比较复杂,目前GC的实现有好多种,敝人只知道引数GC,下面就用lua来说说它。先说明一点:在脚本语言中变量的声明都是使用上面说的第三种方法 - malloc来手动创建内存块。

lua代码:
[cpp] view plain copy
  1. function test()  
  2. local a = {x=1};   -- GC将给"{x=1}"手动创建的这块内存块增加一个引用 - b,引用数为1  
  3. end  
  4. test(); -- 函数执行完后清栈,局部变量a被清掉,GC将给"{x=1}"这块内存将减少一个引用,引用数为0,引用数为0时GC就清理掉"{x=1}"这块内存。  
  5. print(a);   -- nil  

再看下面:

[cpp] view plain copy
  1. b = 0;   -- 外部变量  
  2. function test()  
  3. local a = {x=1};  
  4. b = a;   -- GC将给"{x=1}"这块内存再加一个引用 - a,引用数为2  
  5. end  
  6. test(); -- 函数执行完后清栈,"{x=1}"这块内存引用数减1还剩1,那这块内存块不用清理。  
  7. print(b); -- table: 003BAAD8  


上面的例子你应该大致清楚GC的工作原理了吧,下面看最后此文的关键:lua - weak table
在lua中任何对内存块的引用都会使引用数加1,但有一个例外:weak table,它对内存块的引用不会使引用增1
看下面代码:
[cpp] view plain copy
  1. a = {};  
  2. b = {};  
  3. setmetatable(a,b); -- 设置a为weak table  
  4. b.__mode = 'k';  
  5. key = {}; -- 增加"{}"内存块的一个引用 - key,引用数1  
  6. a[key] = 1; -- weak table引用不增引数,所以"{}"内存块的引数还为1  
  7. key = {}   -- 改变key指向新增的"{}"内存块,上面的"{}"内存块引数减一为0  
  8. a[key] = 2     -- 如上上一样  
  9.   
  10. collectgarbage();   -- 调用GC,清掉weak表中没有引用的内存,如不主动调用,可能引用为0的也不立即回收,输出1和2  
  11.   
  12. for k, v in pairs(a) doprint(v);    -- 只输出一个2  
  13. end  


如果a不是weak table而是普通的table,那么a将会对"{}"内存块的引数加1,去掉"b.__mode = 'k';"这句,你将会看到输出1和2。


看最后一个例子:

[cpp] view plain copy
  1. a = {}  
  2. b = {}  
  3. setmetatable(a, b)  
  4. b.__mode = "k"  
  5.   
  6. function test()      
  7. local key1 = {};     
  8.  a[key1] = 1;      
  9.  local key2 = {};   
  10.  a[key2] = 2;  
  11. end  
  12.    
  13. test(); -- 函数执行完后key1和key2它们指向的内存块引数都减一为0  
  14. collectgarbage() -- 故全部都被清空  
  15. for k, v in pairs(a)  
  16.  doprint(v); -- 没有任何输出  
  17. end  



以上是我自己对gc的理解(没去查资料^_^),仅供参考...
后语:GC虽然是救世主,但不是万能的救世主,在回收变量中存在一些问题,如效率、循环引用等等需要去关注...
原创粉丝点击