【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弱引用表

如果要保存一些活跃对象,该怎么做呢?我们只需要将它放入table,但一个对象一旦加入数table,将再也不能被回收,因为就算没有其他地方引用它,但依然包含在数table中!不过我们可以通过**弱引用表(weak table)**显式告诉Lua,这个数table中的引用不应该影响此对象的回收。

弱引用(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]是存于table当中的,而table中的键值对即使有空也不允许GC删除,而这样的情况就会导致内存泄漏。因此为了避免这种情况,我们可以使用弱引用来告诉GC机制:虽然它是table,但里面的空键值都是可以删除的!

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch Query DSL

Elasticsearch Query DSL 这里使用的 Elasticsearch 的版本为 7.12.1。 1、基本概念 1.1 文档(Document) ElasticSearch 是面向文档的,文档是所有可搜索数据的最小单位,例如 MySQL 的一条数据记录。 文档会被序列化成为 json 格式,保存在…

iPhone 安装 iOS 17公测版(Public Beta)

文章目录 步骤1. 备份iPhone资料步骤2. 申请iOS 17 公测Beta 资格步骤3. 下载iOS 16 Beta 公测描述档步骤4. 选择iOS 17 Beta 公测描述档更新项目步骤5. 升级iOS 17 Public Beta 公开测试版 苹果已经开始向大众释出首个iOS 17 公开测试版/ 公测版( iOS 17 Public Beta)&#xf…

测试|Selenium之WebDriver常见API使用

测试|Selenium之WebDriver常见API使用 文章目录 测试|Selenium之WebDriver常见API使用1.定位对象(findElement)css定位xpath定位css选择器语法:xpath语法:校验结果 2.操作对象鼠标点击对象在对象上模拟按键输入clear清除对象输入的文本内容su…

【Python】Web学习笔记_flask(1)——getpost

flask提供的request请求对象可以实现获取url或表单中的字段值 GET请求 从URL中获取name、age两个参数 from flask import Flask,url_for,redirect,requestappFlask(__name__)app.route(/) def index():namerequest.args.get(name)agerequest.args.get(age)messagef姓名:{nam…

[css]margin-top不起作用问题(外边距合并)

在初学css时&#xff0c;会遇到突然间margin-top不起作用的情况。如下面&#xff1a; 情况一&#xff1a; 代码&#xff1a; <html> <head><style type"text/css"> * {margin:0;padding:0;border:0; }#outer {width:300px;height:300px;backgroun…

ifcfg-ens33中的ONBOOT字段是什么作用?

在CentOS或其他基于Red Hat Enterprise Linux (RHEL)的Linux发行版中&#xff0c;ifcfg-ens33是网络配置文件&#xff0c;用于配置网卡设备ens33的网络参数。ifcfg-ens33文件位于/etc/sysconfig/network-scripts/目录下&#xff08;可能因系统版本而略有不同&#xff0c;例如/e…

Elasticsearch Java客户端和Spring data elasticsearch

文章目录 官网版本组件版本说明实现代码地址es Spring Data Repositories例子&#xff1a;ElasticsearchRepository分析 es Spring Data Repositories 关键字es Spring Data Repositories client 加载rest风格客户端直接执行dsl例子响应式客户端-ReactiveElasticsearchClientpo…

打卡力扣题目七

#左耳听风 ARST 打卡活动重启# 目录 一、题目 二、解题方法一 三、解题方法二 关于 ARTS 的释义 —— 每周完成一个 ARTS&#xff1a; ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一个技术技巧 ● Share:…

【MTK平台】【wpa_supplicant】关于wpa_supplicant_8/src/p2p/p2p_go_neg.c文件的介绍

本文主要介绍external/wpa_supplicant_8/src/p2p/p2p_go_neg.c文件 这里主要介绍2个方法 1. p2p_connect_send接受来自 p2p.c 文件中调用p2p_connect方法发送的GON Request帧 2. p2p_process_go_neg_resp处理来自GON Response帧的处理流程 先看下p2p_connect_send方法 int p…

数据结构:顺序表(C实现)

个人主页 水月梦镜花 个人专栏 C语言 &#xff0c;数据结构 文章目录 一、顺序表二、实现思路1.存储结构2.初始化顺序表(SeqListInit)3.销毁顺序表(SeqListDestroty)4.打印顺序表(SeqListPrint)5.顺序表尾插(SeqListPushBack)and检查容量(SeqListCheckCapacity)6.顺序表头插(Se…

K8S群集调度

目录 一、调度约束二、Pod 启动典型创建过程三、K8S的调度过程3.1 Predicate&#xff08;预选策略&#xff09; 常见的算法使用3.2 常见优先级选项3.3 指定调度节点3.3.1 nodeName指定3.3.2 nodeSelector指定3.3.3 Pod亲和性与反亲和1.节点亲和硬策略示例2.节点亲和软策略示例3…

QEMU源码全解析18 —— QOM介绍(7)

接前一篇文章&#xff1a;QEMU源码全解析17 —— QOM介绍&#xff08;6&#xff09; 本文内容参考&#xff1a; 《趣谈Linux操作系统》 —— 刘超&#xff0c;极客时间 《QEMU/KVM》源码解析与应用 —— 李强&#xff0c;机械工业出版社 特此致谢&#xff01; 上一回完成了对…

Redis发布订阅机制学习|kafka相关经验

Redis 发布订阅机制 简介&#xff1a; Redis 发布订阅&#xff08;Pus/Sub&#xff09;是一种消息通信模式&#xff1a;发送者通过 PUBLISH发布消息&#xff0c;订阅者通过 SUBSCRIBE 订阅接收消息或通过UNSUBSCRIBE 取消订阅。主要由「发布者」、「订阅者」、「Channel」三个部…

linux -网络编程-多线程并发服务器

目录 1.三次握手和四次挥手 2 滑动窗口 3 函数封装思想 4 高并发服务器 学习目标&#xff1a; 掌握三次握手建立连接过程掌握四次握手关闭连接的过程掌握滑动窗口的概念掌握错误处理函数封装实现多进程并发服务器实现多线程并发服务器 1.三次握手和四次挥手 思考: 为什么…

[SQL挖掘机] - 多表连接: union

介绍: sql中的union是用于合并两个或多个select语句的结果集的操作符。它将多个查询的结果合并成一个结果集&#xff0c;并自动去除重复的行。请注意&#xff0c;union操作要求被合并的查询返回相同数量和类型的列。 用法: union的基本语法如下&#xff1a; select_stateme…

【javaSE】 面向对象程序三大特性之继承

目录 为什么需要继承 继承的概念 继承的语法 注意事项 父类成员访问 子类中访问父类的成员变量 子类和父类不存在同名成员变量 子类和父类成员变量同名 访问原则 子类中访问父类的成员方法 成员方法名字不同 总结&#xff1a; 成员方法名字相同 总结&#xff1a; …

深入理解JVM:Java使用new创建对象的流程

1、创建对象的几种方式 ①new 对象 ②反射 ③对象的复制 ④反序列化 2、创建对象流程 先看看常量池里面有没有&#xff0c;如果有&#xff0c;就用常量池的看这个类有没有被加载过&#xff0c;如果没有&#xff0c;就执行类加载以及类的初始化。&#xff08;对象的大小&#…

【决策树-鸢尾花分类】

决策树算法简介 决策树是一种基于树状结构的分类与回归算法。它通过对数据集进行递归分割&#xff0c;将样本划分为多个类别或者回归值。决策树算法的核心思想是通过构建树来对数据进行划分&#xff0c;从而实现对未知样本的预测。 决策树的构建过程 决策树的构建过程包括以…

数据结构:单链表的实现(C语言)

个人主页 &#xff1a; 水月梦镜花 个人专栏 &#xff1a; 《C语言》 《数据结构》 文章目录 前言一、单链表实现思路和图解1.节点的定义(SListNode)2.申请一个节点(BuySListNode)3.单链表打印(SListPrint)4.单链表尾插(SListPushBack)5.单链表的头插(SListPushFront)6.单链表的…

【目标检测】基于yolov5的水下垃圾检测(附代码和数据集,7684张图片)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 路虽远,行则将至;事虽难,做则必成。只要有愚公移山的志气、滴水穿石的毅力,脚踏实地,埋头苦干,积跬步以至千里,就…