【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函数:允许控制垃圾回收器的步长
  • 析构器 (最近查询C#的GC条目,微软官方取的中文名叫做终结器)(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,暂且用知乎上的称呼“析构器” (最近查询C#的GC条目,微软官方取的中文名叫做终结器)。当这个定义了元方法的对象被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) do
    print(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) do
    print(v)
end
输出:
2

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

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

相关文章

【雕爷学编程】MicroPython动手做(32)——物联网之MQTT

MQTT (Message Queuing Telemetry Transport)消息队列遥测传输协议,是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。M…

2023 电赛 E 题 激光笔识别有误--使用K210/Openmv/树莓派/Jetson nano实现激光笔在黑色区域的目标检测

1. 引言 1.1 激光笔在黑色区域目标检测的背景介绍 在许多应用领域,如机器人导航、智能家居和自动驾驶等,目标检测技术的需求日益增加。本博客将聚焦于使用K210芯片实现激光笔在黑色区域的目标检测。 激光笔在黑色区域目标检测是一个有趣且具有挑战性的…

cpolar内网穿透外网远程访问本地网站

cpolar内网穿透外网远程访问本地网站 文章目录 cpolar内网穿透外网远程访问本地网站 在现代人的生活中,电脑是离不开的重要设备,大家看到用到的各种物品都离不开电脑的支持。尽管移动电子设备发展十分迅速,由于其自身存在的短板,使…

css, resize 拖拉宽度

效果如下&#xff1a; 可直接复制预览查看属性值: 关键样式属性&#xff1a; resize: horizontal; overflow-x: auto; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content…

Qt应用开发(基础篇)——数值微调输入框QAbstractSpinBox、QSpinBox、QDoubleSpinBox

目录 一、前言 二、QAbstractSpinBox类 1、accelerated 2、acceptableInput 3、alignment 4、buttonSymbols 5、correctionMode 6、frame 7、keyboardTracking 8、readOnly 9、showGroupSeparator 10、specialValueText 11、text 12、wrapping 13、信号 二、Q…

pytorch实战-图像分类(一)(数据预处理)

目录 1.导入各种库 2.数据预处理 2.1数据读取 2.2图像增强 3.构建数据网络 3.1网络构建 3.2读取标签对应的名字 4.展示数据 4.1数据转换 4.2画图 5.模型训练 1.导入各种库 上代码&#xff1a; import os import matplotlib.pyplot as plt %matplotlib inline import nu…

一台电脑给另外一台电脑共享网络

这里写自定义目录标题 有网的电脑上操作一根网线连接两台电脑没网的电脑上 有网的电脑上操作 右键->属性->共享 如同选择以太网&#xff0c;勾选。确认。 一根网线连接两台电脑 没网的电脑上 没网的电脑为mips&麒麟V10 新增个网络配置ww&#xff0c;设置如下。 …

2.05 购物车后台刷新并显示

一.用户登录添加商品使用cookie存入购物车&#xff0c;并把购物车商品传入到后台 步骤1&#xff1a;创建购物车BO对象 public class ShopcartBO {private String itemId;private String itemImgUrl;private String itemName;private String specId;private String specName;p…

7.物联网操作系统互斥信号量

1.使用互斥信号量解决信号量导致的优先级反转&#xff0c; 2.使用递归互斥信号量解决互斥信号量导致的死锁。 3.高优先级主函数中多次使用同一信号量的使用&#xff0c;使用递归互斥信号量&#xff0c;但要注意每个信号量的使用要对应一个释放 优先级翻转问题 优先级翻转功能需…

牛客网Verilog刷题——VL48

牛客网Verilog刷题——VL48 题目答案 题目 在data_en为高期间&#xff0c;data_in将保持不变&#xff0c;data_en为高至少保持3个B时钟周期。表明&#xff0c;当data_en为高时&#xff0c;可将数据进行同步。本题中data_in端数据变化频率很低&#xff0c;相邻两个数据间的变化&…

【计算机视觉|人脸建模】SOFA:基于风格、由单一示例的2D关键点驱动的3D面部动画

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;SOFA: Style-based One-shot 3D Facial Animation Driven by 2D landmarks 链接&#xff1a;SOFA: Style-based One-shot 3D Facial Animation Driven by 2D landmarks | Proceedings of …

磁盘均衡器:HDFS Disk Balancer

HDFS Disk Balancer 背景产生的问题以及解决方法 hdfs disk balancer简介HDFS Disk Balancer功能数据传播报告 HDFS Disk Balancer开启相关命令 背景 相比较于个人PC&#xff0c;服务器一般可以通过挂载多块磁盘来扩大单机的存储能力在Hadoop HDFS中&#xff0c;DataNode负责最…

【数据结构与算法】线索化二叉树

线索化二叉树 n 个节点的二叉链表中含有 n 1 【公式 2n - (n - 1) n 1】个空指针域。利用二叉链表中的空指针域&#xff0c;存放指向该节点在某种遍历次序下的前驱和后继节点的指针&#xff08;这种附加的指针称为“线索”&#xff09;。这种加上了线索的二叉链表称为线索链…

网站是如何进行访问的?在浏览器地址栏输入网址并回车的一瞬间到页面能够展示回来,经历了什么?

这个问题是检验web和计网学习程度的经典问题。 网站访问流程&#xff1a; 1.域名->ip地址 1) 在输入完一个域名之后&#xff0c;首先是检查浏览器自身的DNS缓存是否有相应IP地址映射&#xff0c;如果没有对应的解析记录&#xff0c;浏览器会查找本机的hosts配置文件&…

【Spring Boot】Thymeleaf模板引擎 — Thymeleaf表达式

Thymeleaf表达式 本节介绍Thymeleaf的各种表达式&#xff0c;通过一些简单的例子来演示Thymeleaf的表达式及用法。 1.变量表达式 变量表达式即获取后台变量的表达式。使用${}获取变量的值&#xff0c;例如&#xff1a; <p th:text"${name}">hello</p>…

leetcode 763. 划分字母区间

2023.8.3 本题的关键是要确保同一字母需要在同一片段中&#xff0c;而这就需要关注到每个字母最后一次出现的位置。 思路&#xff1a;用一个哈希表保存每个字母&#xff08;26个&#xff09;最后一次出现的位置。然后从头遍历&#xff0c;不断更新最右边界&#xff0c;直到当前…

一个严肃的话题,ADR会取代WAF和RASP吗?

做安全的人应该都对WAF耳熟能详&#xff0c;也就是我们常说的Web应用防火墙&#xff0c;成为了应用安全防护的明星产品之一。从传统的防火墙、IDS、IPS&#xff0c;再到WAF横空出世&#xff0c;引领技术趋势若干年&#xff0c;这一阶段可以称为应用安全防护1.0时代。作为一款成…

计算机毕设 深度学习疫情社交安全距离检测算法 - python opencv cnn

文章目录 0 前言1 课题背景2 实现效果3 相关技术3.1 YOLOV43.2 基于 DeepSort 算法的行人跟踪 4 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两…

jar命令的安装与使用

场景&#xff1a; 项目中经常遇到使用WinR软件替换jar包中的文件&#xff0c;有时候存在WinRAR解压替换时提示没有权限&#xff0c;此时winRAR不能用还有有什么方法替换jar包中的文件。 方法&#xff1a; 使用jar命令进行修改替换 问题&#xff1a; 执行jar命令报错jar 不…

【从零开始学习JAVA | 第三十七篇】初识多线程

目录 前言&#xff1a; ​编辑 引入&#xff1a; 多线程&#xff1a; 什么是多线程&#xff1a; 多线程的意义&#xff1a; 多线程的应用场景&#xff1a; 总结&#xff1a; 前言&#xff1a; 本章节我们将开始学习多线程&#xff0c;多线程是一个很重要的知识点&#xff…