1.全局环境_G
- Lua将所有的全局变量保存在一个常规的table中,这个table被称为全局环境,并且将这个table保存在一个全局变量_G中,也就是说在脚本中可以用_G获取这个全局table,并且_G._G 和 _G是等价的,在默认情况,Lua在全局环境_G中添加了标准库比如math、函数比如pairs、print等
2.局部环境_ENV
- 编译器在编译所有代码段(chunk)前,在外层创建局部变量_ENV;
- 编译器将所有自由名称var(无local的变量)翻译为_ENV.var;
- 通常,_G和_ENV指向的是同一个表。按定义,_ENV永远指向的是当前的环境;_G通常指向的是全局变量。_ENV是一个局部变量,所有对“全局变量”的访问实际上访问的都是_ENV指向的全局表
- 对于每一个chunk,都有一个叫_ENV的upvalue,此时_ENV的初值就是_G。在chunk内的函数,都会有这个upvalue值。那么在该代码块中加入的非local变量,可以直接通过名称在其他chunk中访问到(当然该chunk的_ENV 也得是 _G)。
- 函数load使用全局环境初始化代码段的第一个upvalue,即Lua语言内部维护的一个普通的表。
- Lua查找变量定义的规则为:局部变量 -> upvalue -> 全局变量
3.string
- (1)计算需要新创建的字符串对应的散列值;
(2)根据散列值找到对应的散列桶,遍历该散列桶的所有元素,如果能够查找到同样的字符串,说明之前已经存在相同的字符串,此时不需要重新分配一份新的字符串数据,直接返回即可。
(3)如果第(2)步没有找到相同的字符串,调用luaS_newlstr
函数创建一个新的字符串 - Lua虚拟机中global_State的strt字段存放着当前系统中的所有字符串
- Lua中的字符串变量存放的是字符串的引用,而不是字符串本身
- 短字符串在Lua虚拟机中只有一份
- 短字符串比较直接比较地址,长字符串先比较长度再比较内容
4.userdata
-
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 、指针和类)存储到 Lua 变量中调用。
-
udata存储userdata的数据
-
userdata分为两类,”full userdata”和”light userdata”。
-
typedef union Udata { L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ struct { CommonHeader; struct Table *metatable; struct Table *env; size_t len; } uv; } Udata;
-
5.table
- Lua table是数组与哈希表的混合结构
- 读:如果key为正整数并且小于数组长度,去数组中查找,否则去哈希表中查找
- 写:(1).先执行一次读操作,如果table中对应的key已经存在,则修改对应的值。(2).对于正整数key,小于数组长度并且数组对应位置为空的情况下,存入数组中。(3).计算key的hash值,根据hash值映射出一个下标,找到对应的哈希桶存入。(4).当出现键冲突时,会用闭散列方法解决冲突,当已经没有空位置的时候,执行rehash操作。(5).rehash:按一定规则重新计算数组与哈希表的大小,重新分配元素。
- 尽量使用数组域,而不是hash
- 尽量创建Table时初始化数据,而不是建空Table一个一个插入,因为每次插入都会执行一次写操作。
-
typedef union TKey { struct { TValuefields; int next; /* for chaining (offset for next node) */ } nk; TValue tvk; } TKey; typedef struct Node { TValue i_val; TKey i_key; } Node; typedef struct Table { CommonHeader; lu_byte flags; /* 1<<p means tagmethod(p) is not present */ lu_byte lsizenode; /* log2 of size of 'node' array */ unsigned int sizearray; /* size of 'array' array */ TValue *array; /* array part */ Node *node; Node *lastfree; /* any free position is before this position */ struct Table *metatable; GCObject *gclist; } Table;
6.Tvalue
- TValue是实现Lua弱数据类型的主要数据结构
- Tvalue是一个结构体,包括联合体value与int tt_,tt_存储数据类型,value存储值
-
union Value { GCObject *gc; /* collectable objects */ void *p; /* light userdata */ int b; /* booleans */ lua_CFunction f; /* light C functions */ lua_Integer i; /* integer numbers */ lua_Number n; /* float numbers */ }; struct lua_TValue { TValuefields; }; #define TValuefields Value value_; int tt_ typedef struct lua_TValue TValue; typedef TValue *StkId; /* index to stack elements */
7.Closure
- closure是funtion在lua运行时的对象,是一个联合体,包含lclosure与cclosure两个结构体
-
typedef struct Proto { CommonHeader; lu_byte numparams; /* number of fixed parameters */ lu_byte is_vararg; lu_byte maxstacksize; /* number of registers needed by this function */ int sizeupvalues; /* size of 'upvalues' */ int sizek; /* size of 'k' */ int sizecode; int sizelineinfo; int sizep; /* size of 'p' */ int sizelocvars; int linedefined; /* debug information */ int lastlinedefined; /* debug information */ TValue *k; /* constants used by the function */ // 保存常量用的数组 Instruction *code; /* opcodes */ // 保存分析之后生成的OpCode数组 struct Proto **p; /* functions defined inside the function */ int *lineinfo; /* map from opcodes to source lines (debug information) */ LocVar *locvars; /* information about local variables (debug information) */ // 保存局部变量的信息 Upvaldesc *upvalues; /* upvalue information */ // 保存UpValue的信息 struct LClosure *cache; /* last-created closure with this prototype */ TString *source; /* used for debug information */ GCObject *gclist; } Proto; # define ClosureHeader \ CommonHeader; lu_byte nupvalues; GCObject *gclist typedef struct CClosure { ClosureHeader; lua_CFunction f; TValue upvalue[1]; /* list of upvalues */ } CClosure; typedef struct LClosure { ClosureHeader; struct Proto *p; UpVal *upvals[1]; /* list of upvalues */ } LClosure; typedef union Closure { CClosure c; LClosure l; } Closure;
8.Lua State
-
lua_newstate函数主要用于创建一个lua_State和global_State的数据结构。
-
lua_State:主线程栈结构,包括数据栈,与调用栈,数据栈实际就是Tvalue的动态数组,调用栈实质上用一个双向链表,链表中的每个节点是用一个CallInfo的结构体来实现,保存着正在调用的函数的运行状态。
-
global_State:全局状态机,维护全局字符串表、内存管理函数、gc等信息
-
lua_State和global_State结构,通过LG的方式进行链接,将全局状态机global_State挂载到lua_State数据结构上
-
global_State结构是完全感知不到的:我们无法用Lua公开的API获取到它的指针、句柄或引用
-
lua_State和global_State之间通过 global_State *l_G 互相建立链接关系。
-
/* ** 'global state', shared by all threads of this state ** lua 全局状态机 ** 作用:管理全局数据,全局字符串表、内存管理函数、 GC 把所有对象串联起来的信息、内存等 */ typedef struct global_State { /* 版本号 */ const lua_Number *version; /* pointer to version number */ /* 内存管理 */ lua_Alloc frealloc; /* Lua的全局内存分配器,用户可以替换成自己的 - function to reallocate memory */ void *ud; /* 分配器的userdata - auxiliary data to 'frealloc' */ /* 线程管理 */ struct lua_State *mainthread; /* 主线程 */ struct lua_State *twups; /* 闭包了当前线程变量的其他线程列表 - list of threads with open upvalues */ /* 字符串管理 */ stringtable strt; /* 字符串table Lua的字符串分短字符串和长字符串 - hash table for strings */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* 字符串缓存 - cache for strings in API */ /* 虚函数表 */ TString *tmname[TM_N]; /* 预定义方法名字数组 - array with tag-method names */ struct Table *mt[LUA_NUMTAGS]; /* 每个基本类型一个metatable(整个Lua最重要的Hook机制) - metatables for basic types */ /* 错误处理 */ lua_CFunction panic; /* to be called in unprotected errors */ TString *memerrmsg; /* memory-error message */ /* GC管理 */ unsigned int gcfinnum; /* number of finalizers to call in each GC step */ int gcpause; /* size of pause between successive GCs */ int gcstepmul; /* GC 'granularity' */ l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ lu_mem GCmemtrav; /* memory traversed by the GC */ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ TValue l_registry; unsigned int seed; /* randomized seed for hashes */ lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ lu_byte gcrunning; /* true if GC is running */ GCObject *allgc; /* list of all collectable objects */ GCObject **sweepgc; /* current position of sweep in list */ GCObject *finobj; /* list of collectable objects with finalizers */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *weak; /* list of tables with weak values */ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ GCObject *allweak; /* list of all-weak tables */ GCObject *tobefnz; /* list of userdata to be GC */ GCObject *fixedgc; /* list of objects not to be collected */ } global_State;
-
struct lua_State { CommonHeader; unsigned short nci; /* number of items in 'ci' list */ lu_byte status; StkId top; /* first free slot in the stack */ global_State *l_G; CallInfo *ci; /* call info for current function */ const Instruction *oldpc; /* last pc traced */ StkId stack_last; // 指向stack末尾存放的EXTRA_STACK个数组的起始位置 StkId stack; /* stack base */ UpVal *openupval; /* list of open upvalues in this stack */ GCObject *gclist; struct lua_State *twups; /* list of threads with open upvalues */ struct lua_longjmp *errorJmp; /* current error recover point */ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ volatile lua_Hook hook; ptrdiff_t errfunc; /* current error handling function (stack index) */ int stacksize; int basehookcount; int hookcount; unsigned short nny; /* number of non-yieldable calls in stack */ unsigned short nCcalls; /* number of nested C calls */ l_signalT hookmask; lu_byte allowhook; };
9.Lua GC
- lua在每次分配新的内存时,主动调用luaC_checkGC检查是否满足GC条件
-
三色标记法
白色:当前白色——待回收状态、非当前白——待访问状态
灰色:代标记状态,GC已访问,但该对象引用的其他对象还没有被标记
黑色:不可回收状态
过程详解:
1.新建对象,将其颜色标记为非当前白色
2.达到GC条件(自动、手动),遍历根节点(mainthread主线程, 注册表registry, 全局元表metatable, 上次GC循环中剩余的finalize中的对象)引用的对象,开始标记
3.当GC遍历到对象A,就将其标记为灰色,然后去遍历对象A引用了哪些可回收对象
4.当对象A引用的所有可回收对象都被标记以后, 对象A被标记为黑色
5.GC递归执行3,4步骤直到遍历完所有可达的对象
6.遍历global_State的allgc链表所有对象,删除白色对象
10.Lua源码编译