概述

本文会介绍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动态类型的实现方式的一部分。

在接下来,会对常见的几种数据结构进行更为详细一点的分析。