【Lua学习笔记】Lua进阶——垃圾回收
按照唐老师的课程本来要讲自带库的,但是想想这东西能看文档,ctrl+左键还能看注解,并且最重要的许多自带库的方法基本大部分语言都有,其实看看就能懂了。所以还是重点讲讲垃圾回收
文章目录
- GC
- 辅助垃圾回收
- collectgarbage
- 增量模式
- 分代模式
- `__gc`
- weak table弱引用表
以下大部分内容摘抄自[Lua]垃圾回收详解,lua源码解析——gc实现机制[详细版](一),请各位阅读链接中文章
GC
如果你是Unity接触Lua的话,应当知道C#中也存在GC机制,在面向对象的大部分语言中都使用到了虚拟机,而Unity中也使用了Mono 虚拟机。而我们的代码则是在虚拟机的虚拟内存中,在虚拟机中代码要转换成指令集,编译成字节码,最后在各个平台上使用字节码转译的指令集来实现跨平台。
由于对象所占用的内存空间都是存放在虚拟内存中的,因此就不会影响到物理内存。当物理内存需要调用对象的时候,虚拟内存块就会被指派给物理内存。如果一个虚拟内存块长时间不被物理内存所引用,那么它所占的内存应当被释放,这就是GC(garage collect)
Luc官方doc
根据官方的描述,通过GC机制Lua可以自动的管理内存,剔除出那些dead objects,这些object的类型包括:string,tables,userdata,function,threads,internal structures等等
而那些被认为是dead的对象将不会再程序中被访问(但finalizers可以复活这些死亡的物体)。GC认为的已死和程序员认为的有所不同,GC认为长时间不活动就是死了,而如果一个对象被认为死了,那么也就无法再正常访问。
通过使用函数collectgarbage
或者定义元方法__gc
,我们可以直接使用或者重写gc机制。
辅助垃圾回收
虽然自动垃圾回收在大多数时候都适用,但在一些特殊的情况下还是要我们自己确定垃圾回收的对象和时机。为此,Lua语言提供了一下方式来辅助进行垃圾回收
- collectgarbage函数:允许控制垃圾回收器的步长
- 析构器(finalizer):允许收集不在垃圾回收器直接控制下的外部对象
- 弱引用表(weak table):允许收集Lua中还可以程序访问的对象(尤其是table中的空键值)
collectgarbage
---@alias gcoptions
---|>"collect" # 做一次完整的垃圾收集循环。
---| "stop" # 停止垃圾收集器的运行。
---| "restart" # 重启垃圾收集器的自动运行。
---| "count" # 以 K 字节数为单位返回 Lua 使用的总内存数。
---| "step" # 单步运行垃圾收集器。 步长“大小”由 `arg` 控制。
---| "isrunning" # 返回表示收集器是否在工作的布尔值。
---| "incremental" # 改变收集器模式为增量模式。
---| "generational" # 改变收集器模式为分代模式。---
---这个函数是垃圾收集器的通用接口。 通过参数 opt 它提供了一组不同的功能。
---
function collectgarbage(opt, ...) end// 使用方法collectgarbage("加内关键字")
collectgarbage("collect") --主动进行一次垃圾回收(垃圾回收会回收nil的垃圾)
每次垃圾回收占用内存还是挺多的,所以能不用尽量少用,能手动就别自动。
collectgarbage
提供了两种回收模式,分别是增量模式和分代模式
增量模式
在增量模式下,每次gc循环使用mark-and-sweep方法来逐步进行垃圾标记和收集,GC与解释器一起交替运行(Lua5.1及之后,不需要停止主程序的运行),每当解释器分配了一定数量的内存时,垃圾回收器也执行一步。每个GC周期由四个阶段组成,分别是标记(mark)、清理(cleaning)、清除(sweep)和析构(finalization):
collectgarbage("incremental",200,200,13)
1)、garbage-collector pause, 什么时间执行,比上次回收后内增加的比例 默认200% 最大1000%
2)、garbage-collector step multiplier,
相对说内存分配而言的一个比例,也就是是以什么速度收集 默认 200% 最大 1000%
3)、 the garbage-collector step size
控制每次回收的步幅?解释器分配的字节数 默认是 2的13次 约 8K
以下大部分内容摘自lua源码解析——gc实现机制[详细版](一)
上述参数将控制collectgarbage
在增量模式下的一些参数,增量模式使用mark-and-sweep方法,也就是先标记后处理,简单地来说使用的是三色标记法,把所有的对象都放在树形结构,而父子关系可以用一个链表,用头插法进行元素增加。
在起始状态,所有的节点颜色都是白色。白色代表了未引用
现在引用关系如上图所示,那么从根节点开始遍历引用关系,并给链表中的节点上色
1号节点有引用,所以上色灰色,将其头插到链表gray中
接着1号的孩子4号有引用,将1号上色黑色并出链表,再把四号上色灰色并进链表
接着就重复上述过程,4号上色黑色,出链表,789上色灰色入链表。依次执行知道gray链表中再无节点为止,那么所有引用的节点都被上了黑色。
最后就是清理环节,sweep节点会顺序遍历rootgc链表,所有的白色节点都会被提出,如果在清除前有白色节点突然被引用了,那么该节点会被上色保护色白色,不删除它。整理完毕后再把所有黑色节点置为白色,方便下次清理。
分代模式
collectgarbage("generational",20,200)
1),minor比例数,相对于上次major回收增长的比例,达到即执行minor , 默认20% 最大200%
2), major 比例数,内存使用增长比例达到就执行回收,默认100,最大1000
上述参数将控制collectgarbage
在迭代模式下的一些参数,在分代GC模式中,垃圾收集器频繁的执行小型垃圾回收,每次都从最近创建的对象中进行扫描,清理其中的垃圾,而不是扫描所有对象。如果这种小型GC后内存仍然超出限制,它将暂停程序的运行,遍历所有对象进行GC。
__gc
在元表中同样为gc提供了一种元方法 __gc
,元方法所定义的函数被官方称为finalizer,暂且用知乎上的称呼“析构器”。当这个定义了元方法的对象被gc回收的时候,它会执行析构器中的函数。使用这个元方法,我们可以在某些对象被清理的时候调用析构器的函数,或者为了避免某些对象被清理而将它复活。
例子1:
t = {name = "zhangsan"}
setmetatable(t,{__gc = function (t)print(t.name)
end})
t = nil
--调用t的析构函数,打印zhangsan
在例子1中,打印了zhangsan,但是t被gc清理了,实际过程是:gc开始清理对象->使用析构器,打印t.name(尽管t=nil,但是被析构器短暂的复活了,而执行完析构器之后又会死去)->gc清理
如果一个对象在设置元表时,没有为它添加 __gc 元方法,而是在元表创建完成后添加,那么这个对象在回收时将无法触发 __gc元方法。
t = {name = "zhangsan"}
mt = {}
setmetatable(t,mt)
--先设置元表,再为元表添加__gc元方法
mt.__gc = function (t)print(t.name)
end
t = nil
--不会输出任何值(未执行析构器)
因为析构器要访问被回收的对象,因此Lua需要将这个对象复活(resurrection)。通常这种复活是短暂的,这个对象占用的内存将在下次GC时被释放。但是,如果析构器将这个对象存储在某个全局位置(比如全局变量),那么这种复活就会变成永久的。
t = {name = "zhangsan"}
setmetatable(t,{__gc = function (t)print(t.name)a = t --在析构函数中将它赋值给全局变量
end})
t = nil
collectgarbage() --在此处要手动垃圾回收,否则由于下方还有语句不会执行gc,而a也就不会被赋值了,打印zhangsan
print(a.name) --t引用的对象并未被回收(永久复活),打印zhangsan
weak table弱引用表
如果要保存一些活跃对象,该怎么做呢?我们只需要将它放入数组,但一个对象一旦加入数组,将再也不能被回收,因为就算没有其他地方引用它,但依然包含在数组中!不过我们可以通过**弱引用表(weak table)**显式告诉Lua,这个数组中的引用不应该影响此对象的回收。
弱引用(weak reference)指的是不在垃圾回收器考虑范围内的引用,如果一个对象的引用全是弱引用,那么垃圾回收器会回收这个对象,并删除这些弱引用,弱引用表就是Lua实现弱引用的方式。
一个表是否为弱引用表是由其元表的__mode
字段决定的,它有三种情况,分别是:
- 键弱引用:通过设置
__mode = "k"
,允许垃圾回收器回收它的键,但不允许回收值 - 值弱引用:通过设置
__mode = "v"
,允许垃圾回收器回收它的值,但不允许回收键。也称为瞬表(ephemeron table),只有它的键可访问,值才能被访问。因为当它的键无法访问时,值也会被垃圾回收器从table中移除。 - 键值都为弱引用:通过设置
__mode = "kv"
,键值都允许回收
需要强调的时,在任何情况下,只要table
的键或值被回收,整个键值对都会从table
中移除。
以下内容摘自Lua基础之弱引用
Lua采用垃圾自动回收的内存管理机制,但有时候Lua并不能正确判断对象是否需要被销毁,导致某些需要被销毁的对象一直存在,造成内存泄漏。
a = {}
key = {}
print(a[key])
a[key] = 1
print(a[key])
key = {}
print(a[key])
a[key] = 2
collectgarbage()
for k,v in pairs(a) doprint(k, v)
end
输出:
nil
1
nil
table: 00000000006da000 1
table: 00000000006da380 2
本该销毁的1却没有被销毁,尽管key={}之后a[key]是nil,但是当我们遍历的时候还是得到了1,说明它并没有被销毁。这就是因为这个a[key]是存于一个数组当中的,而数组中的键值对即使有空也不允许GC删除,而这样的情况就会导致内存泄漏。因此为了避免这种情况,我们可以使用弱引用来告诉GC机制:虽然它是数组,但里面的空键值都是可以删除的!
a = {}
b = {__mode = "k"}
setmetatable(a,b)
key = {}
a[key] = 1
key = {}
a[key] = 2
collectgarbage()
for k,v in pairs(a) doprint(v)
end
输出:
2
相关文章:

【Lua学习笔记】Lua进阶——垃圾回收
按照唐老师的课程本来要讲自带库的,但是想想这东西能看文档,ctrl左键还能看注解,并且最重要的许多自带库的方法基本大部分语言都有,其实看看就能懂了。所以还是重点讲讲垃圾回收 文章目录 GC辅助垃圾回收collectgarbage增量模式分…...

session和cookie
cookie和session结合使用 web开发发展至今,cookie和session的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式: 1、存储在服务端:通过cookie存储一个session_id,然后具体的数据则是保…...

P7243 最大公约数
题目 思路 利用曼哈顿原理求离(x,y)最远的点 代码 #include<bits/stdc.h> using namespace std; #define int long long #define INF 0x3f3f3f3f const int maxn2005; int gcd(int a,int b) { return b?gcd(b,a%b):a; } int n,m; i…...

ES6基础知识九:你是怎么理解ES6中Module的?使用场景?
一、介绍 模块,(Module),是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)。 两个基本的特征:外部特征和内部特征 外部特征是指模块跟外部环境联系的接口…...

TensorFlow项目练手(三)——基于GRU股票走势预测任务
项目介绍 项目基于GRU算法通过20天的股票序列来预测第21天的数据,有些项目也可以用LSTM算法,两者主要差别如下: LSTM算法:目前使用最多的时间序列算法,是一种特殊的RNN(循环神经网络)…...

微信小程序页面传值为对象[Object Object]详解
微信小程序页面传值为对象[Object Object]详解 1、先将传递的对象转化为JSON字符串拼接到url上2、在接受对象页面进行转译3、打印结果 1、先将传递的对象转化为JSON字符串拼接到url上 // info为对象 let stationInfo JSON.stringify(info) uni.navigateTo({url: /pages/statio…...

Redis篇
文章目录 Redis-使用场景1、缓存穿透2、缓存击穿3、缓存雪崩4、双写一致5、Redis持久化6、数据过期策略7、数据淘汰策略 Redis-分布式锁1、redis分布式锁,是如何实现的?2、redisson实现的分布式锁执行流程3、redisson实现的分布式锁-可重入4、redisson实…...

Entity Framework(EF)查询
一、In 查询 var list = dbContext.Users.Where(u => new int[] {1, 2, 3, 5,...

使用Pytest生成HTML测试报告
背景 最近开发有关业务场景的功能时,涉及的API接口比较多,需要自己模拟多个业务场景的自动化测试(暂时不涉及性能测试),并且在每次测试完后能够生成一份测试报告。 考虑到日常使用Python自带的UnitTest,所…...

DSA之图(4):图的应用
文章目录 0 图的应用1 生成树1.1 无向图的生成树1.2 最小生成树1.2.1 构造最小生成树1.2.2 Prim算法构造最小生成树1.2.3 Kruskal算法构造最小生成树1.2.4 两种算法的比较 1.3 最短路径1.3.1 两点间最短路径1.3.2 某源点到其他各点最短路径1.3.3 Dijkstra1.3.4 Floyd 1.4 拓扑排…...

[SQL挖掘机] - 窗口函数 - row_number
介绍: row_number() 是一种常用的窗口函数,它为结果集中的每一行分配一个唯一的数字。这个数字的分配基于指定的排序顺序,并且不会跳过相同的排名。 用法: row_number() 函数的语法如下: row_number() over ([partition by 列名1, 列名2,…...

【论文阅读】通过解缠绕表示学习提升领域泛化能力用于主题感知的作文评分
摘要 本文工作聚焦于从领域泛化的视角提升AES模型的泛化能力,在该情况下,目标主题的数据在训练时不能被获得。本文提出了一个主题感知的神经AES模型(PANN)来抽取用于作文评分的综合的表示,包括主题无关(pr…...

二分查找P1873 [COCI2011-2012#5] EKO / 砍树
P1873 [COCI2011-2012#5] EKO / 砍树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 这个题就是给新手练手的,在那个位置上在进行,寻找合适的砍树高度,下面在介绍一个二分查找的模板 int binarySearch(vector<int>& nums, int t…...

【BOOST程序库】正则表达式相关操作
基本概念这里不解释了,代码中详细解释了BOOST程序库中对于正则表达式常用方法的详细用法。 #include <iostream> #include <string>//正则表达式头文件 #include <boost/xpressive/xpressive.hpp>int main() {//声明正则:boost::pres…...

阿里云国际版在使用过程中应该注意什么呢?
为确保系统稳定性,用户不得进行以下操作。否则,阿里云可能无法解决由以下违规操作引起的问题: 1) Windows系统中的PV Drivers 程序不可删除 PV Drivers程序为服务器虚拟化驱动程序,请不要针对该程序进行任何操作,如果删…...

Flutter Provider 共享状态管理
在使用Provider的时候,我们主要关心三个概念: ChangeNotifier:真正数据(状态)存放的地方ChangeNotifierProvider:Widget树中提供数据(状态)的地方,会在其中创建对应的Ch…...

std vector 用法
使用vector,需添加头文件#include,要使用sort或find,则需要添加头文件#include。函数封装在命名空间std中,使用:using namespace std; 1、vector的初始化 std::vector<int> nVec; // 空对象 std::vecto…...

vue vite ts electron ipc addon-napi c arm64
初始化 因网络问题建议使用 cnpm 代替 npm npm init vue # 全选 yes npm i # 进入项目目录后使用 npm i electron electron-builder -D npm i commander -D # 额外组件electron 新建 plugins、src/electron 文件夹 添加 src/electron/background.ts 属于主进程 ipcMain.o…...

机器人科普--AGILOX 叉车
机器人科普--AGILOX 叉车 1 概述2 导航3 驱动轮组4 叉举参考 1 概述 AGILOX 叉车,不需要画地图路径,很厉害。 2 导航 中间路径自由导航,末端规划出轨迹路线,并使用优良的控制器做轨迹追踪。 AGILOX | 10 Min setu…...

Django的生命周期流程图(补充)、路由层urls.py文件、无名分组和有名分组、反向解析(无名反向解析、有名反向解析)、路由分发、伪静态
一、orm的增删改查方法(补充) 1. 查询resmodels.表名(类名).objects.all()[0]resmodels.表名(类名).objects.filter(usernameusername, passwordpassword).all()res models.表名(类名).objects.first() # 判断,判断数据是否有# res如果查询…...

selenium交互代码
一:selenium交互 用selenium打开网页后,也可以做一系列真人的操作,也就是利用selenium和浏览器进行交互,可利用以下几个函数进行操作: input.send_keys() 传递输入内容给某输入框button.click() 点击某按钮browser.e…...

下载远程服务器文件
业务需求:下载某云盘的视频文件存储到本地 测试代码 RequestMapping("testVideo")public String test() {try {SimpleDateFormat DATE_FORMAT new SimpleDateFormat("yyyy/MM/dd/");//组装本地保存地址StringBuilder filePath new StringBuilder(StoreP…...

[SQL挖掘机] - 索引
介绍: 当你在数据库中进行查询时,索引是一种用于提高查询性能的重要工具。索引是对表中的一列或多列进行排序的数据结构,它可以快速定位到满足特定条件的记录,从而减少了查询所需的时间和资源。 在数据库中使用索引的主要好处包括ÿ…...

C++STL库中的list
文章目录 list的介绍及使用 list的常用接口 list的模拟实现 list与vector的对比 一、list的介绍及使用 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 2. list的底层是双向带头循环链表结构,双向带头循…...

【LeetCode 75】第十七题(1493)删掉一个元素以后全为1的最长子数组
目录 题目: 示例: 分析: 代码运行结果: 题目: 示例: 分析: 给一个数组,求删除一个元素以后能得到的连续的最长的全是1的子数组。 我们可以先单独统计出连续为1的子数组分别长度…...

配置IPv6 over IPv4 GRE隧道示例
组网需求 如图1,两个IPv6网络分别通过SwitchA和SwitchC与IPv4公网中的SwitchB连接,客户希望两个IPv6网络中的PC1和PC2实现互通。 其中PC1和PC2上分别指定SwitchA和SwitchC为自己的缺省网关。 图1 配置IPv6 over IPv4 GRE隧道组网图 配置思路 要实现I…...

Google Earth Engine谷歌地球引擎提取多波段长期反射率数据后绘制折线图并导出为Excel
本文介绍在谷歌地球引擎GEE中,提取多年遥感影像多个不同波段的反射率数据,在GEE内绘制各波段的长时间序列走势曲线图,并将各波段的反射率数据与其对应的成像日期一起导出为.csv文件的方法。 本文是谷歌地球引擎(Google Earth Engi…...

第三大的数
414、第三大的数 class Solution {public int thirdMax(int[] nums) {Arrays.sort(nums);int tempnums[0];int ansnums[0];int count 0;// if(nums.length<3){// return nums[nums.length-1];// }// else {for(int inums.length-1;i>0;i--){if (nums[i]>nums[i…...

正则表达式中的方括号[]有什么用?
在正则表达式中,方括号 [] 是用于定义字符集合的元字符。它在正则表达式中有以下作用: 匹配字符集合中的任意一个字符:方括号中列出的字符,表示在这个位置可以匹配这些字符中的任意一个。例如,[abc] 将匹配任意一个字符…...

SQL编写规范
文章目录 1.命名规范:2.库表设计:3.查询数据:4.修改数据:5.索引创建: 1.命名规范: 1.库名、表名、字段名,必须使用小写字母或数字,不得超过30个字符。 2.库名、表名、字段名&#…...