《Programming in Lua 3》读书笔记(二十八)

日期:2014.8.15

PartⅣ The C API

32 Memory Management

Lua是动态的对其数据结构进行管理的。所有数据结构按需求进行增长,最终进行释放。Lua对其内存的使用控制较为严格,当我们关闭了一个Lua state的时候,Lua会立即释放其内存占用。不仅如此,Lua中的所有对象都被垃圾回收器管理,包括tables,strings,和functions,threads,和modules。

Lua进行内存管理的方式对多数程序来说都是挺合适的。但是还是有一些特殊的程序需要做一定的适配,如运行在内存紧张的环境中,或者减少内存回收间隔到最小值。Lua允许从两个等级来对这些情况做适配:低等级的时候,设定Lua使用的内存管理函数进行控制;高等级的时候,可以设定一些参数来控制Lua的垃圾回收器,或者甚至我们可以全靠操控这个回收器。在这一节将会对Lua的内存管理进行介绍。

32.1 The Allocation Function

Lua进行内存分配的时候,既不调用malloc 也不调用 realloc函数,而是通过allocation function 进行内存分配和内存回收,这个函数用户必须在创建lua state的时候提供何时创建的。

之前提到的创建lua状态的函数luaL_newstate是一个带默认内存分配函数的辅助函数。其所带的内存分配函数使用C标准库中的标准malloc - realloc - free 函数,这对大多数程序来说是足够用的。当我们需要对Lua的内存管理进行全盘接管的时候,就需要使用另一个函数:lua_newstate:

lua_State *lua_newstate(lua_Alloc f, void *ud);

这个函数接受两个参数:第一个是一个内存分配函数,第二个参数是user data.通过这个函数创建的state总是通过调用f来实现内存的分配和回收。

lua_Alloc 的被定义为:

typedef void * (*lua_Alloc) (void *ud,void *ptr,size_t osize,size_t nsize);

第一个参数是我们提供给lua_newstate 的user data;第二个参数是被分配或者被回收的内存块的地址;第三个参数是原有的内存块大小;最后一个参数为需要分配的大小。

Lua定义NULL为size为0的内存块。当 nsize 为0的时候,内存管理函数将会将释放 prt指向的内存块然后返回NULL,这样符合了分配的大小为0的要求;当ptr 为NULL的时候,函数将会分配并返回给定大小的内存块;当不能分配给定的大小的时候,将会返回NULL;如果ptr 为NULL且 nsize 为0,之前的两种情况都会发生:返回NULL,内存分配函数不做任何工作。

当ptr 不为 NULL且 nsize 不为0的时候,内存管理函数将会对内存块进行再分配,类似于realloc操作,然后返回新的地址。当发生错误的时候,将会返回NULL。Lua假设内存管理过程中当新的分配大小不小于原有的大小时候是不会发生错误的。

luaL_newstate所用的标准内存管理函数定义如下:

void *l_alloc (void *ud,void *ptr ,size_t osize,size_t nsize)
{
     if (nsize == 0)
     {
          free(ptr);
          return NULL;
     }
     else
          return realloc(ptr,nsize);
}

函数假设free(NULL)不会做任何操作,而realloc(NULL,size) 等于 malloc(size)。ANSI C标准是支持这两个假设的。

可以使用函数lua_getallocf 得到一个Lua state的内存管理器:

lua_Alloc lua_getallocf(lua_State *L,void **ud);

当参数 ud为NULL的时候,函数将会设置 ud为这个state的user state的值。当然,可以通过函数lua_setallocf 来改变一个state的内存管理器:

void lua_setallocf(lua_State *L,lua_Alloc f, void *ud);

这里要记住:任何新的管理器都需要负责管理由原有管理器控制的内存管理工作。或者说,新的管理器是原有管理器的一个包装器。

32.2 The Garbage Collector

直到Lua5.0版本,Lua都是使用简单的mark-sweep 模式的garbage collector(GC).这个回收器有时也被称为"stop the world"回收器。这个意味着,Lua会时不时的停止解释主程序的来执行垃圾回收循环工作。每次循环经历三个步骤:mark,cleaning,sweep。

Lua从其根集合开始标记其对象的alive,包含那些能直接访问的对象:registry和主线程。任何存储在live object中的对象都是能被程序访问到的,也因此被标记为alive。当所有能访问到的对象都被标记为了alive后,mark步骤工作执行结束。

在开始sweep步骤之前,Lua先执行cleaning 步骤,这个步骤与finalizer和weak table有关。首先,先遍历所有由finalization标记的对象看是否有未标记的,被标记为alive的对象会被存储在一个单独的链表中,供finalization 阶段使用。然后,Lua遍历weak table 从中移除那些key或者value未被标记的条目。

sweep阶段将会遍历所有的lua 对象。如果一个对象未被标记为alive的,lua会回收掉。而标记为alive的,lua会清除其标记状态,为下一次回收循环准备。在sweep阶段,lua也会调用finalizer中在cleaning阶段单独存储的那些对象。

在Lua5.1版本中新加入了一个incremental collector,这个回收器与之前的那个回收器工作原理类似,只是不会在其运行过程中停止对主程序的解释。当这个回收器工作的时候,解释器可能会改变某个受回收器影响的对象的访问性,不影响主程序的运行。

Gargage-collector API

Lua提供了一些API接口,让我们对回收器做额外的一些控制:

在C中,使用lua_gc:

int lua_gc(lua_State *L,int what,int data);

在Lua中,使用collectgarbage函数:

collectgarbage(what[ ,data])

两个函数都提供了相同的功能:what 参数代表要干嘛,操作有:

LUA_GCSTOP("stop") :停止回收器,直到再一次用参数"restart"调用collectgarbage(lua_gc);

LUA_GCRESTART("restart") :重启回收器;

LUA_GCCOLLECT("collect") : 执行一个完整的垃圾回收循环,保证所有未能被访问到的对象都被回收和finalized。这个是colectgarbage的默认操作;

LUA_GCSTEP("step") :执行一些垃圾回收工作,这些工作数量等于分配了data byte之后回收器将会做的工作的数量;

LUA_GCCOUNT("count") : 返回Lua当前使用的内存数量(千字节)。也包括没有被回收掉的那些内存数量。

LUA_GCCOUNTB(not available) :返回Lua使用的内存数量(千字节的小数部分。

LUA_GCSETPAUSE("setpause") :设置回收器的pause 参数,使用百分比单位,即当设定的data值为100,将会设定1(100%).用来控制两次回收循环的间隔时间。

LUA_GCSETSTEPMUL("setstepmul"): 设置回收器的step multiplier 参数。也是百分比单位。控制每次回收工作要回收多少数量的内存。

算是马马虎虎,勉勉强强的把这本书看过一遍了吧,看纯英文版的技术类书籍还是有点难度,主要是一些技术概念还不懂,因为之前没有接触过Lua这门编程语言,直接上外文书籍有点吃力,想必经过看这本书的历程,之后再学习应该难度会降低不少吧。

时间: 2024-10-15 10:01:45

《Programming in Lua 3》读书笔记(二十八)的相关文章

《Programming in Lua 3》读书笔记(二十二)

日期:2014.8.6 PartⅣ The C API 26 Extending Your Application 使用Lua很重要的一点是用来做配置语言.配合主语言做一些功能的配置. 26.1 The Basics 有的时候程序需要配置一些功能信息,很多时候可能有许多别的方法比用lua做配置要更简单:如使用环境变量或者读取文件,读取文件涉及到文件的解析.如果使用Lua进行配置的话,相当于用lua文件替代了要读取的如csv.txt文件等. 使用Lua进行配置的时候,就需要使用Lua API去控制

【Unity 3D】学习笔记二十八:unity工具类

unity为开发者提供了很多方便开发的工具,他们都是由系统封装的一些功能和方法.比如说:实现时间的time类,获取随机数的Random.Range( )方法等等. 时间类 time类,主要用来获取当前的系统时间. using UnityEngine; using System.Collections; public class Script_04_13 : MonoBehaviour { void OnGUI() { GUILayout.Label("当前游戏时间:" + Time.t

angular学习笔记(二十八)-$http(6)-使用ngResource模块构建RESTful架构

ngResource模块是angular专门为RESTful架构而设计的一个模块,它提供了'$resource'模块,$resource模块是基于$http的一个封装.下面来看看它的详细用法 1.引入angular-resource.min.js文件 2.在模块中依赖ngResourece,在服务中注入$resource var HttpREST = angular.module('HttpREST',['ngResource']); HttpREST.factory('cardResource

马哥学习笔记二十八——nginx反向代理,负载均衡,缓存,URL重写及读写分离

Nginx反向代理 Nginx通过proxy模块实现反向代理功能.在作为web反向代理服务器时,nginx负责接收客户请求,并能够根据URI.客户端参数或其它的处理逻辑将用户请求调度至上游服务器上(upstream server).nginx在实现反向代理功能时的最重要指令为proxy_pass,它能够将location定义的某URI代理至指定的上游服务器(组)上.如下面的示例中,location的/uri将被替换为上游服务器上的/newuri. location /uri { proxy_pa

angular学习笔记(二十八-附2)-$resource中的promise对象

下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到: HttpREST.factory('cardResource',function($resource){ return $resource('/card/user/:userID/:id',{userID:123,id:'@id'},{charge:{method:'POST',params:{charge:true},isArray:false}}) }); HttpREST.factory('h

angular学习笔记(二十八-附1)-$resource中的资源的方法

通过$resource获取到的资源,或者是通过$resource实例化的资源,资源本身就拥有了一些方法,比如$save,可以直接调用来保存该资源: 比如有一个$resource创建的服务: var service = angular.module('myRecipe.service',['ngResource']); service.factory('Recipe',['$resource',function($resource){ return $resource('/recipe/:id',

《Programming in Lua 3》读书笔记(二十五)

日期:2014.8.11 PartⅣ The C API 29 User-Defined Types in C 在之前的例子里,已经介绍过如果通过用C写函数来扩展Lua.在本章,将会介绍通过用C写新的类型来扩展Lua,将会使用到元方法等特性来实现这个功能. 以一个例子来介绍本章将要介绍的,例子实现的功能是实现了一个简单的类型:boolean arrays.实现这个功能主要是这种方法不需要太复杂的算法,因此可以将精力放在API的讨论上.当然我们可以在Lua中用一个table来实现,但是用一个C来实

《Programming in Lua 3》读书笔记(二十六)

日期:2014.8.12 PartⅣ The C API 30 Managing Resources 上一节中实现的自定义类型,我们并没有关注于资源管理的问题.上一节实现的数组是需要关心内存问题,而这些问题由Lua实现管理.但是很多时候事情不那么简单,有些对象不仅需要内存空间,还会需要如窗口句柄.文件描述等资源.尽管说这些也是内存开销,但是这些资源是由系统的其他组件管理的.这种情况下,当一个对象被回收了,我们也需要合适的机制来实现回收这些额外的资源. 在17.6章节中,介绍了Lua提供的fina

《Distributed Programming With Ruby》读书笔记二 Security and ID Conversion (Part1.1-2)

Security Although DRb provide some security, they fall short of a full, comprehensive solution. This can make DRb less than desirable in a lot of real world situations. However, in situations where security is a lesser concern,such as prototyping and