概述
本文会介绍lua每一个类型的用法,以及其类型在底层的实现,并通过测试来验证内存占用大小。
对于使用者来说,了解不同的类型在底层具体的实现,并不是必须的,但是一个加分项。了解之后,也更有助于对lua这门语言的深入理解。
*本文文章采用的源码是 lua-5.3.4 版本。
Lua 的内存管理方式
lua为一个动态的、对内存进行自动管理的语言。lua对内存进行管理的关键字叫“gc”(garbage collect)。
大致的使用方式是,新构建的object(对象),判断自身是否需要gc。如果是,则使用类似“继承重载可gc对象”的方式,生成新的对象,生成后将此object自动列入到gc列表中。如果不是,则为普通的object,超出作用域或者无有效引用后直接删除。
lua在一些合适的时机(取决于给设定的参数),对自己可gc的部分进行回收。
具体的gc算法较为复杂,后续有机会再详细展开。个人认为,脱离gc后,我们对lua的理解依然是不受影响的,我们只需要大概地知道lua的内存管理方式是怎样的就可以了。
Lua值类型的实现
lua对value object的定义非常清晰,如下:
// lobject.h line.92
/*
** Tagged Values. This is the basic representation of values in Lua,
** an actual value plus a tag with its type.
*/
/*
** Union of all Lua values
*/
typedef 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 */
} Value;注释里比较清楚地写明Value结构的组成,一个Value可以是:
一个可GC的Object;
一个引用到 userdata 的轻量指针;
一个c函数;
一个整数;
一个浮点数。
但从定义来看,我们无法直接知道一个 Value 是什么类型的值,拿到一个值的时候,这个union可以有n种解释(这也叫“非自省”结构)。所以在lua的源码中,大多数情况并不会直接使用 Value这个定义,而是使用了一个标示了类型的、封装了多一层的结构TValue(带 type 信息的 value):
// lobject.h line.110
#define TValuefields Value value_; int tt_
typedef struct lua_TValue {
TValuefields;
} TValue;定义中的 tt_ 便是本Value的类型。tt_ 的集合如下:
// lua.h line.59
/*
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
#define LUA_NUMTAGS 9*Value 的定义是一个 Union,意味着这是一种典型的“多态”式写法。Union中的所有元素共享内存。对Union不熟悉的同学,可以Bing一下。
GCObject 的实现
我们继续展开可GC的Object:
/*
** Common type for all collectable objects
*/
typedef struct GCObject GCObject;
/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
/*
** Common type has only the common header
*/
struct GCObject {
CommonHeader;
};GCObject 主要规定的头部的 CommonHeader(皆可暂时不放心上):
next 指向下一个 gc object,用于在gc过程中,组织垃圾回收链。在此不展开。
tt 标示自己是哪种类型的 object,用以执行不同的 gc 策略,此处的 tt 对应外部TValue 的tt。
marked 标示此object是否要被回收。
在lstate.h中能够找到所有可gc的object的集合:
// lstate.h line.208
/*
** Union of all collectable objects (only for conversions)
*/
union GCUnion {
GCObject gc; /* common header */
struct TString ts;
struct Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct lua_State th; /* thread */
};CommonHeader 的写法是怎么回事呢?GCObject 又是如何跟 table、string 等不同的字类型联系的呢?
设想一下如果是我们自己来设计gcobject,那么table、string等可gc的object,在组织上应该是一种“继承”关系。
其实lua也是这么做的。但在c(低版本标准)中并没有提供直接的类操作,于是CommonHeader 便出现来解决这个问题。其实不止lua,观察一些原生c写就的代码里,有非常多此类的写法。
比如看一下 TString:
// lobject.h line.208
/*
** Header for string value; string bytes follow the end of this structure
** (aligned according to 'UTString'; see next).
*/
typedef struct TString {
CommonHeader;
lu_byte extra; /* reserved words for short strings; "has hash" for longs */
lu_byte shrlen; /* length for short strings */
unsigned int hash;
union {
size_t lnglen; /* length for long strings */
struct TString *hnext; /* linked list for hash table */
} u;
} TString;可以看到TString struct 里便有了 CommonHeader。于是可以理解TString继承了GCObject的元素。
在对TString 进行类型转换之后(类似子类转换成父类),也能够进行正常的解析。
*更多c的类写法可以Bing一下,在后面的标准里,c语言也支持了类。
总结
在了解lua的基础类型的实现之后,大家甚至可以直观地理解了lua动态类型的实现方式的一部分。
在接下来,会对常见的几种数据结构进行更为详细一点的分析。
















