zoukankan      html  css  js  c++  java
  • Lua中的weak表——weak table(转)

      弱表(weak table)是一个很有意思的东西,像C++/Java等语言是没有的。弱表的定义是:A weak table is a table whose elements are weak references,元素为弱引用的表就叫弱表。有弱引用那么也就有强引用,有引用那么也就有非引用。我们先要厘这些基本概念:变量、值、类型、对象。

      (1)变量与值:Lua是一个dynamically typed language,也就是说在Lua中,变量没有类型,它可以是任何东西,而值有类型,所以Lua中没有变量类型定义这种东西。另外,Lua中所有的值都是第一类值(first-class values)。

      (2)Lua有8种基本类型:nil、boolean、number、string、function、userdata、thread、table。其中Nil就是nil变量的类型,nil的主要用途就是一个所有类型之外的类型,用于区别其他7中基本类型。

      (3)对象objects:Tables、functins、threads、userdata。对于这几种值类型,其变量皆为引用类型(变量本身不存储类型数据,而是指向它们)。赋值、参数传递、函数返回等都操作的是这些值的引用,并不产生任何copy行为。

       

      Lua的垃圾回收机制:gc是很多语言的常见机制,让程序员拜托复杂易出错的内存管理。

      定义:Lua manages memory automatically by running a garbage collector to collect all dead objects (that is, objects that are no longer accessible from Lua). 

      三点理解:(1)gc自动运行,也可以手动调用;(2)自动收集的目标是引用计数为0的对象;(3)dead objects:不能访问到的对象,没有引用指向它了,当然就是访问不到的了,也就等同于垃圾内存了。

      weak table的定义:

      (1)weak表是一个表,它拥有metatable,并且metatable定义了__mode字段;

      (2)weak表中的引用是弱引用(weak reference),弱引用不会导致对象的引用计数变化。换言之,如果一个对象只有弱引用指向它,那么gc会自动回收该对象的内存。

      (3)__mode字段可以取以下三个值:k、v、kv。k表示table.key是weak的,也就是table的keys能够被自动gc;v表示table.value是weak的,也就是table的values能被自动gc;kv就是二者的组合。任何情况下,只要key和value中的一个被gc,那么这个key-value pair就被从表中移除了( In any case, if either the key or the value is collected, the whole pair is removed from the table)。

      对于普通的强引用表,当你把对象放进表中的时候,就产生了一个引用,那么即使其他地方没有对表中元素的任何引用,gc也不会被回收这些对象。那么你的选择只有两种:手动释放表元素或者让它们常驻内存。

    复制代码
    strongTable = {}
    strongTable[1] = function() print("i am the first element") end
    strongTable[2] = function() print("i am the second element") end
    strongTable[3] = {10, 20, 30}
    
    print(table.getn(strongTable))            -- 3
    collectgarbage()                        
    print(table.getn(strongTable))            -- 3
    复制代码

      但是,在编程环境中,有时你并不确定手动给一个键值赋nil的时机,而是需要等所有使用者用完以后进行释放,在释放以前,是可以访问这个键值对的。这种时候,weak表就派上用场了。关于weak table的理解,看下面这个小例子:

    复制代码
    weakTable = {}
    weakTable[1] = function() print("i am the first element") end
    weakTable[2] = function() print("i am the second element") end
    weakTable[3] = {10, 20, 30}
    
    setmetatable(weakTable, {__mode = "v"})        -- 设置为弱表
    
    print(table.getn(weakTable))                -- 3
    
    ele = weakTable[1]                    -- 给第一个元素增加一个引用
    collectgarbage()
    print(table.getn(weakTable))               -- 1,第一个函数引用为1,不能gc
    
    ele = nil                             -- 释放引用
    collectgarbage()
    print(table.getn(weakTable))                -- 0,没有其他引用了,全部gc
    复制代码

      当然在实际的代码过程中,我们不一定需要手动collectgarbage,因为该函数是在后台自动运行的,它有自己的运行周期和规律,对编程者来说是透明的。

      注意:只有拥有显示构造的对象类型会被自动从weak表中移除,值类型boolean、number是不会自动从weak中移除的。而string类型虽然也由gc来负责清理,但是string没有显示的构造过程,因此也不会自动从weak表中移除,对于string的内存管理有单独的策略。

      基于weak表的简单应用:

      (1)记忆函数:一个相当普遍的编程技术是用空间来换取时间。你可以通过记忆函数结果来进行优化,当你用同样的参数再次调用函数时,它可以自动返回记忆的结果。将函数的输入和输出分别作为key和value放在一个weak table里面,调用函数之前先查看有无现成的结果,有就返回,没有就调用函数,然后将结果存入表中。由于是weak table,此表会定期自动清理掉不再有引用的键值对。

      (2)关联对象属性:Lua本身使用这种技术来保存数组的大小。table库提供了一个函数来设定数组的大小,另一个函数来读取数组的大小。当你设定了一个数组的大小,Lua 将这个尺寸保存在一个私有的weak table,索引就是数组本身,而value就是它的尺寸。

      同样的,当我们需要给任一对象添加一个属性的时候,可以在外部单独做一弱key表,然后以对象为key值,属性值为value。这样即可以方便的访问这个属性,也不影响该对象的释放。而且对象本身没任何修改,能很好的保持对象本身的独立性。

       (3)带有默认值的表:

      有两种实现方法,第一种方法,使用关联对象属性的方法,将表作为key,默认值作为value,存到一个弱key的weak表中:

    复制代码
    local defaults = {}
    setmetatable(defaults, {_mode = "k"})
    
    local mt = {__index = function(t) return defaults[t] end}
    
    function setDefault(t, d)
        defaults[t] = d
        setmetatable(t, mt)
    end
    复制代码

      第二种方法,针对不同的metatable来进行优化,对于每一个具体的默认值,生成一个与之对应的metatable,然后以默认值为key,metatable为value,存到一个弱value的weak表中:

    复制代码
    metas = {}
    setmetatable(metas, {__mode = "v"})
    
    setdefault = function (t, d)
        local mt = metas[d]
        if mt == nil then
            mt = {__index = function() return d end}
            metas[d] = mt
        end
        setmetatable(t, mt)
    end
    复制代码

      两种方式各有利弊,第一种方法对于每一个table都需要添加一个键值对,但是公用一个metatable。第二种方法需要许多个不同的metatable,但拥有相同默认值的table共用一个metatable,并且weak表要比第一种方法小。如果你的代码环境中有很多个table,但常用默认值只有那么几种,建议选择第二种方法,否则就选择第一种方法。

    1、 Lua脚本中的标签方法由宿主语言即java语言调用,Lua脚本实际上被LuaJ翻译成了java的类实例方法。整个调用过程在一个单线程中运行。
    2、 Java语言调用Lua脚本标签方法时,会传递一些参数,标准参数一般是request或deltaMo。这些参数由调用接口转换成Lua的标签方法的参数,它是一个Lua table结构。比如java的request参数转成Lua参数就是moSelf,而deltaMo参数转换成Lua就是incData。

    http://www.cnblogs.com/sifenkesi/p/3850760.html

  • 相关阅读:
    CodeForces 19D Points (线段树+set)
    FZU 2105 Digits Count
    HDU 5618 Jam's problem again(三维偏序,CDQ分治,树状数组,线段树)
    HDU 5634 Rikka with Phi (线段树)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
  • 原文地址:https://www.cnblogs.com/softidea/p/5242970.html
Copyright © 2011-2022 走看看