当前位置: 首页 > news >正文

【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
控制每次回收的步幅?解释器分配的字节数 默认是 213次 约 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 最大公约数

题目 思路 利用曼哈顿原理求离&#xff08;x&#xff0c;y&#xff09;最远的点 代码 #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的?使用场景?

一、介绍 模块&#xff0c;&#xff08;Module&#xff09;&#xff0c;是能够单独命名并独立地完成一定功能的程序语句的集合&#xff08;即程序代码和数据结构的集合体&#xff09;。 两个基本的特征&#xff1a;外部特征和内部特征 外部特征是指模块跟外部环境联系的接口…...

TensorFlow项目练手(三)——基于GRU股票走势预测任务

项目介绍 项目基于GRU算法通过20天的股票序列来预测第21天的数据&#xff0c;有些项目也可以用LSTM算法&#xff0c;两者主要差别如下&#xff1a; LSTM算法&#xff1a;目前使用最多的时间序列算法&#xff0c;是一种特殊的RNN&#xff08;循环神经网络&#xff09;&#xf…...

微信小程序页面传值为对象[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分布式锁&#xff0c;是如何实现的&#xff1f;2、redisson实现的分布式锁执行流程3、redisson实现的分布式锁-可重入4、redisson实…...

Entity Framework(EF)查询

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

使用Pytest生成HTML测试报告

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

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() 是一种常用的窗口函数&#xff0c;它为结果集中的每一行分配一个唯一的数字。这个数字的分配基于指定的排序顺序&#xff0c;并且不会跳过相同的排名。 用法: row_number() 函数的语法如下&#xff1a; row_number() over ([partition by 列名1, 列名2,…...

【论文阅读】通过解缠绕表示学习提升领域泛化能力用于主题感知的作文评分

摘要 本文工作聚焦于从领域泛化的视角提升AES模型的泛化能力&#xff0c;在该情况下&#xff0c;目标主题的数据在训练时不能被获得。本文提出了一个主题感知的神经AES模型&#xff08;PANN&#xff09;来抽取用于作文评分的综合的表示&#xff0c;包括主题无关&#xff08;pr…...

二分查找P1873 [COCI2011-2012#5] EKO / 砍树

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

【BOOST程序库】正则表达式相关操作

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

阿里云国际版在使用过程中应该注意什么呢?

为确保系统稳定性&#xff0c;用户不得进行以下操作。否则&#xff0c;阿里云可能无法解决由以下违规操作引起的问题&#xff1a; 1) Windows系统中的PV Drivers 程序不可删除 PV Drivers程序为服务器虚拟化驱动程序&#xff0c;请不要针对该程序进行任何操作&#xff0c;如果删…...

Flutter Provider 共享状态管理

在使用Provider的时候&#xff0c;我们主要关心三个概念&#xff1a; ChangeNotifier&#xff1a;真正数据&#xff08;状态&#xff09;存放的地方ChangeNotifierProvider&#xff1a;Widget树中提供数据&#xff08;状态&#xff09;的地方&#xff0c;会在其中创建对应的Ch…...

std vector 用法

使用vector&#xff0c;需添加头文件#include&#xff0c;要使用sort或find&#xff0c;则需要添加头文件#include。函数封装在命名空间std中&#xff0c;使用&#xff1a;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 叉车&#xff0c;不需要画地图路径&#xff0c;很厉害。 2 导航 中间路径自由导航&#xff0c;末端规划出轨迹路线&#xff0c;并使用优良的控制器做轨迹追踪。 AGILOX &#xff5c; 10 Min setu…...

Django的生命周期流程图(补充)、路由层urls.py文件、无名分组和有名分组、反向解析(无名反向解析、有名反向解析)、路由分发、伪静态

一、orm的增删改查方法&#xff08;补充&#xff09; 1. 查询resmodels.表名(类名).objects.all()[0]resmodels.表名(类名).objects.filter(usernameusername, passwordpassword).all()res models.表名(类名).objects.first() # 判断&#xff0c;判断数据是否有# res如果查询…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...