跟着 Lua 5.1 官方参考文档学习 Lua (5)

文章目录

    • 2.10 – Garbage Collection
      • 2.10.1 – Garbage-Collection Metamethods
      • 2.10.2 – Weak Tables

2.10 – Garbage Collection

Lua performs automatic memory management. This means that you have to worry neither about allocating memory for new objects nor about freeing it when the objects are no longer needed. Lua manages memory automatically by running a garbage collector from time to time to collect all dead objects (that is, objects that are no longer accessible from Lua). All memory used by Lua is subject to automatic management: tables, userdata, functions, threads, strings, etc.


补充:

Since its first version until version 5.0, Lua always used a simple mark-andsweep garbage collector. This collector is sometimes called a “stop-the-world” collector. This means that, from time to time, Lua stops interpreting the main program to perform a whole garbage-collection cycle. Each cycle comprises four phases: mark, cleaning, sweep, and finalization.

Lua starts the mark phase marking as alive its root set, the objects that Lua has direct access to: the registry and the main thread. Any object stored in a live object is reachable by the program, and therefore is marked as alive too. The mark phase ends when all reachable objects are marked as alive.

Before starting the sweep phase, Lua performs the cleaning phase, which is related to finalizers and weak tables.

First, it traverses all userdata looking for non-marked userdata with a __gc metamethod; those userdata are marked as alive and put in a separate list, to be used in the finalization phase.

Second, Lua traverses its weak tables and removes from them all entries wherein either the key or the value is not marked.

The sweep phase traverses all Lua objects. (To allow this traversal, Lua keeps all objects it creates in a linked list.) If an object is not marked as alive, Lua collects it. Otherwise, Lua clears its mark, in preparation for the next cycle.

The last phase, finalization, calls the finalizers of the userdata that were separated in the cleaning phase. This is done after the other phases to simplify error handling. A wrong finalizer may throw an error, but the garbage collector cannot stop during other phases of a collection, at the risk of leaving Lua in an inconsistent state. If it stops during this last phase, however, there is no problem: the next cycle will call the finalizers of the userdata that were left in the list.

With version 5.1 Lua got an incremental collector. This new incremental collector performs the same steps as the old one, but it does not need to stop the world while it runs. Instead, it runs interleaved with the interpreter. Every time the interpreter allocates some fixed amount of memory, the collector runs a small step. This means that, while the collector works, the interpreter may change an object’s reachability. To ensure the correctness of the collector, some operations in the interpreter have barriers that detect dangerous changes and correct the marks of the objects involved.

To avoid too much complexity, the incremental collector performs some operations atomically; that is, it cannot stop while performing those operations. In other words, Lua still “stops the world” during an atomic operation. If an atomic operation takes too long to complete, it may interfere with the timing of your program. The main atomic operations are table traversal and the cleaning phase.

The atomicity of table traversal means that the collector never stops while traversing a table. This can be a problem only if your program has a really huge table. If you have this kind of problem, you should break the table in smaller parts. (That may be a good idea even if you do not have problems with the garbage collector.) A typical reorganization is to break the table hierarchically, grouping related entries into subtables. Notice that what matters is the number of entries in the table, not the size of each entry.

The atomicity of the cleaning phase implies that the collector collects all userdata to be finalized and clears all weak tables in one step. This can be a problem if your program has huge quantities of userdata or huge numbers of entries in weak tables (either in a few large weak tables or in countless weak tables).


Lua implements an incremental mark-and-sweep collector. It uses two numbers to control its garbage-collection cycles: the garbage-collector pause and the garbage-collector step multiplier. Both use percentage points as units (so that a value of 100 means an internal value of 1).

The garbage-collector pause controls how long the collector waits before starting a new cycle. Larger values make the collector less aggressive. Values smaller than 100 mean the collector will not wait to start a new cycle. A value of 200 means that the collector waits for the total memory in use to double before starting a new cycle.


补充:

The pause parameter controls how long the collector waits between finishing a collection and starting a new one. Lua uses an adaptive algorithm to start a collection: given that Lua is using m Kbytes when a collection ends, it waits until it is using m*pause Kbytes to start a new collection. So, a pause of 100% starts a new collection as soon as the previous one ends. A pause of 200% waits for memory usage to double before starting the collector; this is the default value. You can set a lower pause if you want to trade more CPU time for lower memory usage. Typically, you should keep this value between 100% and 300%.


The step multiplier controls the relative speed of the collector relative to memory allocation. Larger values make the collector more aggressive but also increase the size of each incremental step. Values smaller than 100 make the collector too slow and can result in the collector never finishing a cycle. The default, 200, means that the collector runs at “twice” the speed of memory allocation.


补充:

The stepmul parameter controls how much work the collector does for each kilobyte of memory allocated. The higher this value the less incremental is the collector. A huge value like 100000000% makes the collector work like a nonincremental collector. The default value is 200%. Values lower than 100% make the collector so slow that it may never finish a collection.


You can change these numbers by calling lua_gc in C or collectgarbage in Lua. With these functions you can also control the collector directly (e.g., stop and restart it).


补充:

Lua offers an API that allows us to exert some control over the garbage collector.
From C we use lua_gc:

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

From Lua we use the collectgarbage function:

collectgarbage(what [, data])

Both offer the same functionality. The what argument (an enumeration value in C, a string in Lua) specifies what to do. The options are:
LUA_GCSTOP (“stop”): stops the collector until another call to collectgarbage (or to lua_gc) with the option “restart”, “collect”, or “step”.

LUA_GCRESTART (“restart”): restarts the collector.

LUA_GCCOLLECT (“collect”): performs a complete garbage-collection cycle, so that all unreachable objects are collected and finalized. This is the default option for collectgarbage.

LUA_GCSTEP (“step”): performs some garbage-collection work. The amount of work is given by the value of data in a non-specified way (larger values mean more work).

LUA_GCCOUNT (“count”): returns the number of kilobytes of memory currently in use by Lua. The count includes dead objects that were not collected yet.

LUA_GCCOUNTB (not available): returns the fraction of the number of kilobytes of memory currently in use by Lua.

In C, the total number of bytes can be computed by the next expression (assuming that it fits in an int):

lua_gc(L, LUA_GCCOUNT, 0)*1024 + lua_gc(L, LUA_GCCOUNTB, 0)

In Lua, the result of collectgarbage(“count”) is a floating-point number, and the total number of bytes can be computed as follows:

collectgarbage("count") * 1024

So, collectgarbage has no equivalent to this option.

LUA_GCSETPAUSE (“setpause”): sets the collector’s pause parameter. The value is given by data in percentage points: when data is 100 the parameter is set to 1 (100%).

LUA_GCSETSTEPMUL (“setstepmul”): sets the collector’s stepmul parameter. The value is given by data also in percentage points.

The other options of lua_gc give you more explicit control over the collector.

Games are typical clients for this kind of control. For instance, if you do not want any garbage-collection work during some periods, you can stop it with a call collectgarbage(“stop”) and then restart it with collectgarbage(“restart”).

In systems where you have periodic idle phases, you can keep the collector stopped and call collectgarbage(“step”,n) during the idle time. To set how much work to do at each idle period, you can either choose experimentally an appropriate value for n or calls collectgarbage in a loop, with n set to zero (meaning small steps), until the period expires.


2.10.1 – Garbage-Collection Metamethods

Using the C API, you can set garbage-collector metamethods for userdata (see §2.8). These metamethods are also called finalizers. Finalizers allow you to coordinate Lua’s garbage collection with external resource management (such as closing files, network or database connections, or freeing your own memory).

Garbage userdata with a field __gc in their metatables are not collected immediately by the garbage collector. Instead, Lua puts them in a list. After the collection, Lua does the equivalent of the following function for each userdata in that list:

     function gc_event (udata)
       local h = metatable(udata).__gc
       if h then
         h(udata)
       end
     end

At the end of each garbage-collection cycle, the finalizers for userdata are called in reverse order of their creation, among those collected in that cycle. That is, the first finalizer to be called is the one associated with the userdata created last in the program. The userdata itself is freed only in the next garbage-collection cycle.

2.10.2 – Weak Tables

A weak table is a table whose elements are weak references. A weak reference is ignored by the garbage collector. In other words, if the only references to an object are weak references, then the garbage collector will collect this object.

A weak table can have weak keys, weak values, or both. A table with weak keys allows the collection of its keys, but prevents the collection of its values. A table with both weak keys and weak values allows the collection of both keys and values. In any case, if either the key or the value is collected, the whole pair is removed from the table.

The weakness of a table is controlled by the __mode field of its metatable. If the __mode field is a string containing the character ‘k’, the keys in the table are weak. If __mode contains ‘v’, the values in the table are weak.

After you use a table as a metatable, you should not change the value of its __mode field. Otherwise, the weak behavior of the tables controlled by this metatable is undefined.


补充:

A garbage collector can collect only what it can be sure is garbage; it cannot guess what you consider garbage.

A typical example is a stack, implemented with an array and an index to the top. You know that the valid part of the array goes only up to the top, but Lua does not. If you pop an element by simply decrementing the top, the object left in the array is not garbage for Lua. Similarly, any object stored in a global variable is not garbage for Lua, even if your program will never use it again. In both cases, it is up to you (i.e., your program) to assign nil to these positions so that they do not lock an otherwise free object.

However, simply cleaning your references is not always enough. Some constructions need extra collaboration between the program and the collector.

A typical example happens when you want to keep a collection of all live objects of some kind (e.g., files) in your program. This task seems simple: all you have to do is to insert each new object into the collection. However, once the object is inside the collection, it will never be collected! Even if no one else points to it, the collection does. Lua cannot know that this reference should not prevent the reclamation of the object, unless you tell Lua about this fact.

Weak tables are the mechanism that you use to tell Lua that a reference should not prevent the reclamation of an object. A weak reference is a reference to an object that is not considered by the garbage collector. If all references pointing to an object are weak, the object is collected and somehow these weak references are deleted. Lua implements weak references as weak tables: A weak table is a table whose entries are weak. This means that, if an object is held only inside weak tables, Lua will eventually collect the object.

Tables have keys and values, and both may contain any kind of object. Under normal circumstances, the garbage collector does not collect objects that appear as keys or as values of an accessible table. That is, both keys and values are strong references, as they prevent the reclamation of objects they refer to. In a weak table, keys and values may be weak. This means that there are three kinds of weak tables: tables with weak keys, tables with weak values, and fully weak tables, where both keys and values are weak. Irrespective of the table kind, when a key or a value is collected the whole entry disappears from the table .

例子:弱表

-- 创建一个弱表
local weakTable = setmetatable({}, {__mode = "v"})  -- "v" 表示值是弱引用

-- 创建一个对象
local obj = {name = "some object"}

-- 将对象插入到弱表中
weakTable[1] = obj

-- 此时,obj 还存在于 weakTable 中,并且没有其他引用指向它
print(weakTable[1].name)  -- 输出: some object

-- 删除强引用
obj = nil

-- 因为 obj 没有其他强引用,Lua 会在下一次垃圾回收时回收它
collectgarbage()  -- 强制进行垃圾回收

-- 现在,weakTable[1] 会变成 nil,因为 obj 被回收
print(weakTable[1])  -- 输出: nil

例子:弱表

a = {}
b = {__mode = "k"}
setmetatable(a, b) -- now ’a’ has weak keys
key = {} -- creates first key
a[key] = 1
key = {} -- creates second key
a[key] = 2
collectgarbage() -- forces a garbage collection cycle
for k, v in pairs(a) do print(v) end
--> 2

Notice that only objects can be collected from a weak table. Values, such as numbers and booleans, are not collectible. For instance, if we insert a numeric key in table a (from our previous example), it will never be removed by the collector. Of course, if the value corresponding to a numeric key is collected, then the whole entry is removed from the weak table.

From the programmer’s point of view, string s are values, not objects. Therefore, like a number or a boolean, a string is not removed from weak tables (unless its associated value is collected).

A common programming technique is to trade space for time. You can speed up some functions by memoizing their results so that, later, when you call the function with the same argument, it can reuse the result.

例子:

-- the server can memoize the results from loadstring using an auxiliary table.
local results = {}
-- The savings with this scheme can be huge. However, it may also cause unsuspected waste.
-- A weak table providesa simple solution to this problem. If the results table has weak values, each
-- garbage-collection cycle will remove all translations not in use at that moment
-- (which means virtually all of them)
setmetatable(results, {__mode = "v"}) -- make values weak

function mem_loadstring (s)
    local res = results[s]
    if res == nil then -- result not available?
        res = assert(loadstring(s)) -- compute new result
        results[s] = res -- save for later reuse
    end
    return res
end

The memoize technique is useful also to ensure the uniqueness of some kind of object. For instance, assume a system that represents colors as tables, with fields red, green, and blue in some range. A naive color factory generates a new color for each new request:

function createRGB (r, g, b)
    return {red = r, green = g, blue = b}
end

Using the memoize technique, we can reuse the same table for the same color. To create a unique key for each color, we simply concatenate the color indices with a separator in between:

local results = {}
setmetatable(results, {__mode = "v"}) -- make values weak
function createRGB (r, g, b)
    local key = r .. "-" .. g .. "-" .. b
    local color = results[key]
    if color == nil then
        color = {red = r, green = g, blue = b}
        results[key] = color
    end
    return color
end

An interesting consequence of this implementation is that the user can compare colors using the primitive equality operator, because two coexistent equal colors are always represented by the same table.

Another important use of weak tables is to associate attributes with objects. There are endless situations where we need to attach some attribute to an object: names to functions, default values to tables, sizes to arrays, and so on.

In all these cases, we need an alternative way to associate attributes to objects. Of course, an external table provides an ideal way to associate attributes to objects (it is not by chance that
tables are sometimes called associative arrays). We use the objects as keys, and their attributes as values. An external table can keep attributes of any type of object, as Lua allows us to use any type of object as a key. Moreover, attributes kept in an external table do not interfere with other objects, and can be as private as the table itself.

However, this seemingly perfect solution has a huge drawback: once we use an object as a key in a table, we lock the object into existence. Lua cannot collect an object that is being used as a key. If we use a regular table to associate functions to its names, none of these functions will ever be collected. As you might expect, we can avoid this drawback by using a weak table. This time,
however, we need weak keys. The use of weak keys does not prevent any key from being collected, once there are no other references to it. On the other hand, the table cannot have weak values; otherwise, attributes of live objects could be collected.

How to implement tables with non-nil default values.

In the first solution, we use a weak table to associate to each table its default value:

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

In the second solution, we use distinct metatables for distinct default values, but we reuse the same metatable whenever we repeat a default value. This is a typical use of memoizing:

local metas = {}
setmetatable(metas, {__mode = "v"})
function setDefault (t, d)
    local mt = metas[d]
    if mt == nil then
        mt = {__index = function () return d end}
        metas[d] = mt -- memoize
    end
    setmetatable(t, mt)
end

We use weak values, in this case, to allow the collection of metatables that are not being used anymore.

If your application has thousands of tables with a few distinct default values, the second implementation is clearly superior. On the other hand, if few tables share common defaults, then you should favor the first implementation.


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/975906.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jupyter里面的manim编程学习

1.Jupyterlab的使用 因为我之前一直都是使用的vscode进行manim编程的,但是今天看的这个教程使用的是Jupyter,我也很是好奇这个manim在Jupyter这样的交互式下面会生成怎么样的效果,所以今天尝试了jupyter,并且对于两个进行比较和说…

孜然单授权系统V2.0PHP授权系统

孜然单授权V1.0系统,延续了2022年开发的孜然多应用授权系统V2.0 变更:多应用变单系统,去除没用的垃圾代码,从0开发,去除了一些没用的功能 完善了开发文档,之前那套是我写着玩的屎山代码,V1.0将展…

输入菜单关键字,遍历匹配到 menuIds,展开 匹配节点 的所有父节点以及 匹配节点 本身,高亮 匹配节点

菜单检索,名称、地址、权限标志 等 关键字匹配、展开、高亮(全程借助 DeepSeek ) 便捷简洁的企业官网 的后台菜单管理,图示: 改造点: (1)修改 bootstrapTreeTable 的节点class命名方式为:treeg…

【落羽的落羽 数据结构篇】顺序结构的二叉树——堆

文章目录 一、堆1. 概念与分类2. 结构与性质3. 入堆4. 出堆 二、堆排序三、堆排序的应用——TOP-K问题 一、堆 1. 概念与分类 上一期我们提到,二叉树的实现既可以用顺序结构,也可以用链式结构。本篇我们来学习顺序结构的二叉树,起个新名字—…

数据结构系列一:初识集合框架+复杂度

前言 数据结构——是相互之间存在一种或多种特定关系的数据元素的集合。数据结构是计算机专业的基础课程,但也是一门不太容易学好的课,它当中有很多费脑子的东西,之后在学习时,你若碰到了困惑或不解的地方 都是很正常的反应&…

Python 入门教程(2)搭建环境 | 2.3、VSCode配置Python开发环境

文章目录 一、VSCode配置Python开发环境1、软件安装2、安装Python插件3、配置Python环境4、包管理5、调试程序 前言 Visual Studio Code(简称VSCode)以其强大的功能和灵活的扩展性,成为了许多开发者的首选。本文将详细介绍如何在VSCode中配置…

VSCode自定义快捷键和添加自定义快捷键按键到状态栏

VSCode自定义快捷键和添加自定义快捷键按键到状态栏 📄在VSCode中想实现快捷键方式执行与某些指令操作进行绑定,可以通过配置组合式的键盘按键映射来实现,另外一种方式就是将执行某些特定的指令嵌入在面板菜单上,在想要执行的时候…

Linux系统安装MySQL5.7(其他版本类似)避坑指南

1.远程连接 在Linux系统安装好MySQL5.7数据库,不要以为就大功告成了后面还有大坑等着你踩了。宏哥这里介绍一下远程连接遇到的坑以及如何处理。由于征文要求安装环境教学除外宏哥这里就不介绍在Linux系统安装mysql数据库,有需要的可以自己百度一下。但是…

HybridCLR+Adressable+Springboot热更

本文章会手把手教大家如何搭建HybridCLRAdressableSpringboot热更。 创作不易,动动发财的小手点个赞。 安装华佗 首先我们按照官网的快速上手指南搭建一个简易的项目: 快速上手 | HybridCLR 注意在热更的代码里添加程序集。把用到的工具放到程序集里…

C语言(12)--------->for循环

在C语言中,有三大结构:顺序、选择、循环。这些结构可以用于处理生活中各种各样的复杂问题。选择结构通常是用if语句或者switch语句实现,可参考前面的博客: C语言(7)------------>if语句CSDN C…

react路由总结

目录 一、脚手架基础语法(16~17) 1.1、hello react 1.2、组件样式隔离(样式模块化) 1.3、react插件 二、React Router v5 2.1、react-router-dom相关API 2.1.1、内置组件 2.1.1.1、BrowserRouter 2.1.1.2、HashRouter 2.1.1.3、Route 2.1.1.4、Redirect 2.1.1.5、L…

JAVA最新版本详细安装教程(附安装包)

目录 文章自述 一、JAVA下载 二、JAVA安装 1.首先在D盘创建【java/jdk-23】文件夹 2.把下载的压缩包移动到【jdk-23】文件夹内,右键点击【解压到当前文件夹】 3.如图解压会有【jdk-23.0.1】文件 4.右键桌面此电脑,点击【属性】 5.下滑滚动条&…

拆解微软CEO纳德拉战略蓝图:AI、量子计算、游戏革命如何改写未来规则!

2025年2月19日 知名博主Dwarkesh Patel对话微软CEO萨蒂亚纳德拉 在最新访谈释放重磅信号:AI将掀起工业革命级增长,量子计算突破引爆材料科学革命,游戏引擎进化为世界模拟器。 整个视频梳理出几大核心观点,揭示科技巨头的未来十年…

记录此刻:历时两月,初步实现基于FPGA的NVMe SSD固态硬盘存储控制器设计!

背景 为满足实验室横向项目需求,在2024年12月中下旬导师提出基于FPGA的NVMe SSD控制器研发项目。项目核心目标为:通过PCIe 3.0 x4接口实现单盘3000MB/s的持续读取速率。 实现过程 调研 花了半个月的时间查阅了一些使用FPGA实现NVME SSD控制器的论文、…

Grok 3与GPT-4.5的“智能天花板”争夺战——谁才是大模型时代的算力之王?

2025年2月18日,马斯克旗下 xAI 高调发布新一代大模型Grok 3,号称“地球上最聪明AI”,在数学推理、代码生成等核心能力上碾压 GPT-4o、DeepSeek-V3 等对手。而就在同一天,OpenAI创始人 Sam Altman 暗示 GPT-4.5 即将登场&#xff0…

Window电脑中 Linux 系统配置VMware固定IP【最新详细】

一、为什么需要固定IP 当前我们虚拟机的Linux操作系统,其IP地址是通过DHCP服务获取的,DHCP:动态获取IP地址,即每次重启设备后都会获取一次,可能导致IP地址频繁变更。 原因1:办公电脑IP地址变化无所谓,但是…

网络安全高级软件编程技术

安全软件开发入门 软件安全问题 有趣的《黑客帝国》终极解释: 《黑客帝国》故事里面的人物关系,就像电脑里面的各种程序的关系一样: 电脑里面的系统程序:Matrix; 病毒程序:以Neo为首的人类; …

【AI学习笔记】2月10日李飞飞巴黎AI峰会演讲:探索 AI 的历史、现状与未来

【AIGC学习笔记】2月10日李飞飞巴黎AI峰会演讲:探索 AI 的历史、现状与未来 AI 的历史根基与发展历程 生命起源与智能诞生:5 亿年前视觉概念的出现推动了智能的诞生。最初的感知仅仅是被动的体验,只是但随着神经系统的活跃,视觉…

一文读懂Docker之Docker Compose

目录 一、Docker Compose简介 二、Docker Compose的安装和基本使用 1、Docker Compose的安装 步骤一、下载docker-compose 步骤二、新增可执行权限 步骤三、查看是否安装成功 2、Docker Compose的基本使用 (1)、docker-compose up (2)、docker-compose ps (3)、docke…

算法常见八股问题整理

1.极大似然估计和交叉熵有什么关系 在分类问题中,当我们使用softmax函数作为输出层时,最大化对数似然函数实际上等价于最小化交叉熵损失函数。具体来说,在多分类情况下,最大化该样本的对数似然等价于最小化该样本的交叉熵损失。 交…