Lua的垃圾回收机制详解
Lua 是一种轻量级的编程语言,广泛用于嵌入到其他应用程序中,尤其是在游戏开发领域。Lua 的内存管理机制采用了自动垃圾收集(Garbage Collection)的方法。以下是Lua内存管理的一些关键方面:
垃圾收集原理概述
Lua 使用的是标记-清除(Mark-and-Sweep)算法进行垃圾收集。这个过程分为两个阶段:
- 标记(Mark)阶段:Lua 遍历所有活动对象(即那些仍然可以从根集合直接或间接访问的对象),并将它们标记为活动的。
- 清除(Sweep)阶段:Lua 移除所有未被标记的对象,释放它们占用的内存。
内存分配
Lua 使用 malloc 和 free(C语言标准库函数)进行内存分配和释放。
内存泄漏
尽管 Lua 提供了自动垃圾收集,但内存泄露仍然可能发生,尤其是在使用复杂的数据结构和循环引用时。程序员需要注意正确管理对象的生命周期,使用弱引用表来帮助打破潜在的循环引用。
弱引用表
弱引用表,简称弱表,是一种特殊类型的表,其键值对中的键(key)和/或值(value)可以是弱引用。这意味着,如果一个对象只被弱表所引用,那么它不会被视为活跃对象,因此可以被垃圾收集器回收。
弱表的行为通过设置其元表(metatable)中的 __mode 字段来控制。__mode 字段可以有以下设置:
"k":如果设置为"k",则表中的键是弱引用。这意味着,如果一个对象只作为键存在于表中,它可以被回收。"v":如果设置为"v",则表中的值是弱引用。这意味着,如果一个对象只作为值存在于表中,它可以被回收。"kv"或"vk":在这种情况下,键和值都是弱引用。
弱表的使用示例
如果一个对象只被弱表引用,一旦程序的其他部分不再引用该对象,它就会成为垃圾收集的候选对象。
这种特性使弱表成为实现自动缓存机制的理想选择。在缓存场景中,您可能希望暂时存储一些数据以提高效率,但如果这些数据不再被需要,它们应该自动释放,以避免不必要地占用内存。
假设您正在开发一个应用程序,需要频繁地对某些对象进行昂贵的计算。为了提高效率,您决定缓存这些计算结果。但是,您不希望缓存永久占用内存,特别是当原始对象不再需要时。
以下是一个实现这种缓存机制的 Lua 代码示例:
-- 创建一个值为弱引用的表
local cache = setmetatable({}, { __mode = "v" })function expensiveComputation(obj)-- 执行一些昂贵的计算-- ...return result
endfunction getCachedResult(obj)-- 首先检查结果是否已经在缓存中local result = cache[obj]if not result then-- 如果不在缓存中,执行计算并将结果存储在缓存中result = expensiveComputation(obj)cache[obj] = resultend-- 返回缓存或新计算的结果return result
end-- 使用示例
local myObject = {}
local result = getCachedResult(myObject)-- 当 myObject 不再被其他地方引用时,它以及其对应的缓存结果将自动被垃圾收集器清除
在这个例子中,cache 是一个弱表,用于存储昂贵计算的结果。当一个对象(如 myObject)传递给 getCachedResult 函数时,该函数首先检查是否已经有缓存结果。如果没有,它将执行计算并将结果存储在 cache 中。
由于 cache 是一个弱引用表,所以一旦 myObject 不再被程序的其他部分引用,它和其对应的缓存结果将自动成为垃圾收集的候选,从而释放相关内存。这样,缓存仅在数据实际需要时占用内存,避免了长期持有不再需要的数据导致的内存泄露。
使用弱表打破循环引用
在 Lua 中,使用弱引用表可以有效地帮助打破循环引用,从而避免内存泄露。循环引用发生在两个或多个对象互相持有对方的引用,导致它们都无法被垃圾收集器回收。弱引用表是一种特殊的表,其中的引用不会阻止垃圾收集器回收引用的对象。
假设我们有两个对象,A 和 B,它们互相持有对方的引用。这就形成了一个循环引用。
local A = {}
local B = {}A.other = B
B.other = A
在上面的代码中,A 持有对 B 的引用,B 也持有对 A 的引用。如果不采取措施,这将导致 A 和 B 都无法被垃圾收集器回收。
为了解决这个问题,我们可以使用弱引用表。我们将其中一个对象(比如 B)放入一个弱引用表中。
local A = {}
local weakTable = setmetatable({}, {__mode = "v"}) -- 创建一个值为弱引用的表local B = {}
weakTable[1] = B -- 把 B 存储在弱引用表中A.other = weakTable[1]
B.other = A
在这个例子中,B 存储在一个值为弱引用的表 weakTable 中。这意味着 weakTable 对 B 的引用不会阻止 B 被垃圾收集器回收。一旦外部对 B 的所有强引用(如直接引用)都消失,B 将可以被垃圾收集器回收,尽管 A 通过 weakTable 间接引用它。这样,我们就打破了循环引用,避免了内存泄露。
增量收集策略
在 Lua 中,垃圾回收器的增量收集(Incremental Collection)策略是为了减少垃圾收集过程对程序执行的干扰。传统的垃圾收集(如完全标记-清除或停止-复制算法)可能会在收集过程中暂停整个程序,尤其是在处理大量数据时,这种暂停会导致明显的性能问题。
增量收集的工作原理
增量垃圾收集通过将垃圾收集过程分解为多个小步骤来工作,而不是一次性完成所有工作。这些小步骤在程序的正常执行过程中逐渐完成,从而避免了长时间的程序暂停。这对于需要高响应性的应用程序,如游戏或实时系统,尤其重要。
Lua 的垃圾收集器主要通过以下步骤实现增量收集:
-
标记阶段的分解:在标记阶段,垃圾收集器逐渐标记活动对象。而不是一次性遍历所有对象。在每次程序的小暂停期间,它只标记一部分对象,然后让程序继续执行。
-
可调整的收集频率:Lua 允许调整垃圾收集器的工作频率。通过调整,可以控制垃圾收集器在程序执行中占用的比例,从而平衡性能和内存使用。
-
清扫阶段的分解:在清扫阶段,垃圾收集器逐步释放未标记的对象。这个过程也是分步进行的,每次执行释放一小部分对象。
调整和控制
Lua 提供了API(如 collectgarbage 函数)来调整垃圾收集器的行为,包括触发完整的垃圾收集循环、设置垃圾收集器的步进大小等。这些控制手段允许开发者根据具体应用的需要定制垃圾收集器的行为,优化性能和内存使用。
三色垃圾回收
三色垃圾回收是一种在增量收集中使用的标记策略。它通过将对象标记为三种颜色(白色、灰色、黑色)来追踪垃圾收集过程中的对象状态。这种方法允许垃圾回收器在程序的正常运行过程中逐步执行标记和清除操作。

三色标记法的原理
-
灰色(Gray):
- 表示对象已经被标记,但是其引用的对象还没有被完全检查。
- 灰色对象可能引用白色对象,所以不能直接清除。
-
白色(White):
- 表示对象尚未被标记。
- 白色对象可能是垃圾,因为没有灰色或黑色对象引用它们。
-
黑色(Black):
- 表示对象已被标记,并且该对象引用的所有对象也都已经被检查。
- 黑色对象不会引用任何白色对象,所以可以被安全清除。
三色垃圾回收的过程
在增量垃圾收集过程中,Lua 使用三色标记法来保证在整个回收过程中保持一致性。过程如下:
-
初始阶段:
- 所有对象最初都是白色。
- 当垃圾收集开始时,从根集合(如全局变量、活跃的函数调用栈等)出发,将可达对象标记为灰色。
-
标记阶段:
- 将灰色对象转变为黑色,同时将它们直接引用的对象(如果是白色)标记为灰色。
- 这个过程逐步进行,直到没有更多的灰色对象为止。
-
清扫阶段:
- 所有剩余的白色对象都被视为垃圾并被清除。
- 然后,收集器准备下一次收集,通常是通过将所有黑色对象转变为白色来实现。
分代垃圾收集
Lua 5.4 引入了分代垃圾收集(Generational Garbage Collection)机制,这是对其标准标记-清除(Mark-and-Sweep)垃圾回收算法的一个重要优化。分代垃圾收集基于这样一个观察:对象的生存时间往往有很大的差异,大多数对象在创建后不久就不再被使用(成为垃圾),而一些对象则可能存活得更久。
分代垃圾收集的基本原理
分代收集的基本理念是将对象分为几个“代”(generations),根据它们的存活时间对它们进行不同的处理。在 Lua 中,主要分为两代:
-
新生代(Young Generation):
- 这一代包括最近创建的对象。
- 新生代的对象经常进行垃圾收集,因为许多新创建的对象很快就不再被需要。
-
老年代(Old Generation):
- 长时间存活的对象被移动到老年代。
- 这些对象不会经常进行垃圾收集,因为一旦它们存活了一定时间,就很有可能会继续存活。
分代收集的过程
分代垃圾收集的过程大致如下:
-
新对象的分配:
- 最初,所有新创建的对象都被放在新生代。
-
新生代的收集:
- 新生代的垃圾收集频率相对较高。
- 这是一种“次要垃圾收集”(Minor GC),通常只涉及新生代的对象。
-
晋升(Promotion):
- 如果一个对象在新生代中存活足够长的时间(即在多次垃圾收集后仍然存活),它会被晋升到老年代。
- 晋升是为了减少在这个对象上花费的垃圾收集努力,因为它很可能会继续存活。
-
老年代的收集:
- 这是一种“主要垃圾收集”(Major GC),涉及整个内存(包括新生代和老年代)。
- 老年代的垃圾收集频率比新生代低。
优势
- 性能提升,减少暂停时间:
考虑因素
- 分代收集增加了垃圾收集的复杂性,需要仔细平衡新生代和老年代的大小以及晋升策略,以实现最佳性能。
- 在某些情况下,分代收集可能会导致内存使用效率稍微下降,因为一些长期存活的对象可能占据内存较长时间。
总的来说,分代垃圾收集是 Lua 在垃圾收集领域的一个重要进步,它通过智能地管理不同寿命的对象,提高了内存管理的效率和程序的整体性能。
相关文章:
Lua的垃圾回收机制详解
Lua 是一种轻量级的编程语言,广泛用于嵌入到其他应用程序中,尤其是在游戏开发领域。Lua 的内存管理机制采用了自动垃圾收集(Garbage Collection)的方法。以下是Lua内存管理的一些关键方面: 垃圾收集原理概述 Lua 使用…...
java设计模式学习之【解释器模式】
文章目录 引言解释器模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用表达式解析示例代码地址 引言 在我们的日常生活中,语言的翻译和理解是沟通的关键。每种语言都有自己的语法规则,而翻译人员和计算机程序需要理解并遵循这些规则来…...
Unity中Shader旋转矩阵(四维旋转矩阵)
文章目录 前言一、围绕X轴旋转1、可以使用上篇文章中,同样的方法推导得出围绕X轴旋转的点阵。2、求M~rotate~ 二、围绕Y轴旋转1、可以使用上篇文章中,同样的方法推导得出围绕Y轴旋转的点阵。2、求M~rotate~ 三、围绕Z轴旋转1、可以使用上篇文章中&#x…...
【大数据】Centos 7安装教程
一、下载VMware 大家可以通过浏览器进入官网下载VMware,下载后打开VMware进行安装。 二、下载镜像的方式 1、进入Centos官网下载 2、进入阿里云、华为云镜像站下载 以阿里云为例,这里有很多,比如ubuntu、centos,点进去就可以选…...
2024 年 11 款最佳 Android 数据恢复软件应用
Android 设备上的数据丢失可能是一种令人痛苦的经历,通常会导致不可替代的信息瞬间消失。 意外删除、系统崩溃或格式错误都可能发生,重要数据的丢失可能会扰乱日常工作并影响您的工作效率。 幸运的是,技术进步带来了多种恢复解决方案&…...
Redis 核心知识总结
Redis 核心知识总结 认识 Redis 什么是 Redis? Redis 是一个由 C 语言开发并且基于内存的键值型数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景。 有以下几个特…...
Android Jetpack之用Room+ViewModel+LiveData实现增删改查数据(createFromAsset())
文章目录 一、Room简介二、用RoomViewModelLiveData增删改查数据三、下载源码 一、Room简介 Room是Google推出的数据库框架,是一个 ORM (Object Relational Mapping)对象关系映射数据库、其底层还是对SQLite的封装。 Room包含三个主要组件: 数据库类&…...
MySQL ORDER BY(排序) 语句-读取的数据进行排序
MySQL ORDER BY(排序) 语句 我们知道从 MySQL 表中使用 SELECT 语句来读取数据。 如果我们需要对读取的数据进行排序,我们就可以使用 MySQL 的 ORDER BY 子句来设定你想按哪个字段哪种方式来进行排序,再返回搜索结果。 MySQL ORDER BY(排序) 语句可以…...
【ES】es介绍
倒排索引(Inverted Index)和正排索引(Forward Index) 正排索引是一种以文档为单位的索引结构,它将文档中的每个单词或词组与其所在的文档进行映射关系的建立。正排索引通常用于快速检索指定文档的内容,可以…...
07.kubernetes客户端部署
kubernetes 客户端部署 主要是配置 kubectl 完成以下两个操作: 首先是要实现通过命令行连接到Kubernetes的apiserver然后就是创建必要的 ClusterRoleBinding 实现 kubelet bootstrapping CSR 的自动验签kubelet bootstrapping主要涉及以下两个问题,官方文档已经给出详细的介…...
laravel5.8中实现验证码组件的安装和验证
本篇文章主要讲解使用laravel5.8自带的验证码库实现验证码验证的效果教程。通过本教程你可以快速接入到自己的项目中开发相应的验证功能。 作者:任聪聪 (rccblogs.com) 日期:2023年12月17日 实际效果 安装步骤 步骤一、输入命令 composer require mews…...
使用VScode通过内网穿透在公网环境下远程连接进行开发
文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…...
常用的 linux 命令
常用的 linux 命令 1.从其他机器拷贝文件夹2.查看哪个程序在用特定端口3.实时监控日志文件内容4.查看指定用户拥有的进程5.查看磁盘空间使用情况6.文件搜索which(whereis) 显示系统命令所在目录find 查找任何文件或目录1) 根据文件名称查找2)…...
[论文阅读笔记28] 对比学习在多目标跟踪中的应用
这次做一篇2D多目标跟踪中使用对比学习的一些方法. 对比学习通过以最大化正负样本特征距离, 最小化正样本特征距离的方式来实现半监督或无监督训练. 这可以给训练MOT的外观特征网络提供一些启示. 使用对比学习做MOT的鼻祖应该是QDTrack, 本篇博客对QDTrack及其后续工作做一个总…...
Ubuntu 下播放语音提示
目录 一、安装语音库 二、生成音频文件 三、语音播放代码 一、安装语音库 sudo apt update apt-get install libasound2-dev二、生成音频文件 # 文字生成 MP3网地:https://www.text-to-speech.cn/# MP3 转 WAV网址:https://www.aconvert.com/cn/aud…...
ubuntu 用户管理
ubuntu 用户管理 用户组管理用户管理VNC 远程桌面参考 用户组管理 # 查看所有组信息 cat /etc/group # 查看当前用户所在组 groups # 添加用户组 sudo groupadd uav# 添加ostest用户到 uav 用户组 需要注销并重新登录 sudo gpasswd -a ostest uav sudo usermod -aG uav ostes…...
轻舟已过万重山,鸿蒙4.0程序员危机
现在是2023年末。自从华为推出的鸿蒙系统到现在已经有4年多。之前的鸿蒙系统只是基于Android套壳,因为这也也被无数人瞧不起,自从华为秋季发布会后,宣布鸿蒙4.0问世。不再兼容Android,华为做独立的系统终于打了翻身仗。 鸿蒙系统…...
【Pytorch】学习记录分享6——PyTorch经典网络 ResNet与手写体识别
【Pytorch】学习记录分享5——PyTorch经典网络 ResNet 1. ResNet (残差网络)基础知识2. 感受野3. 手写体数字识别3. 0 数据集(训练与测试集)3. 1 数据加载3. 2 函数实现:3. 3 训练及其测试: 1. ResNet &…...
Flink1.17实战教程(第三篇:时间和窗口)
系列文章目录 Flink1.17实战教程(第一篇:概念、部署、架构) Flink1.17实战教程(第二篇:DataStream API) Flink1.17实战教程(第三篇:时间和窗口) Flink1.17实战教程&…...
CSS 纵向扩展动画
上干货 <template><!-- mouseenter"startAnimation" 表示在鼠标进入元素时触发 startAnimation 方法。mouseleave"stopAnimation" 表示在鼠标离开元素时触发 stopAnimation 方法。 --><!-- 容器元素 --><div class"container&q…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...
DAY 45 超大力王爱学Python
来自超大力王的友情提示:在用tensordoard的时候一定一定要用绝对位置,例如:tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾: tensorboard的发展历史和原理tens…...
