Unity官方文档中关于内存管理的翻译(2021.3)
原文:Memory in Unity - Unity 手册
Unity内存管理
为了确保您的应用程序运行时没有性能问题,了解Unity如何使用和分配内存非常重要。本文档的这一部分解释了Unity中内存是如何工作的,适用于希望了解如何提高应用程序内存性能的读者。
Unity使用三个内存管理层来处理应用程序中的内存:
1. 托管内存:一个受控的内存层,使用托管堆和垃圾收集器来自动分配和分配内存。
2. C#非托管内存:与Unity Collections命名空间和包一起使用的内存管理层。这种内存类型称为“非托管”,因为它不使用垃圾收集器来管理未使用的内存部分。
3. 本地内存:Unity用于运行引擎的C++内存。在大多数情况下,这个内存对Unity用户是不可访问的,但如果您想要微调应用程序性能的某些方面,了解它可能会有用。
托管内存
Mono和IL2CPP的脚本虚拟机(VM)实现了托管内存系统,有时也称为脚本内存系统。这些VM提供了一个受控内存环境,分为以下不同类型:
- 托管堆:VM自动控制的内存部分,使用垃圾收集器(GC)来管理。因此,分配在托管堆上的内存被称为GC分配。分配的任何发生都会在Profiler中记录为GC.Alloc样本。
- 脚本堆栈:当应用程序进入和退出任何代码范围时,堆栈会构建和解开。
- 本地VM内存:包含与Unity的脚本层相关的内存。在大多数情况下,您不需要操作本地VM内存,但了解它包括与代码生成相关的执行代码内存,尤其是关于泛型使用、Reflection使用的类型元数据以及运行VM所需的内存。
由于托管内存系统使用VM,它具有受控环境,自动跟踪分配的引用以管理其生命周期。这意味着您的应用程序不太可能在其他代码尝试访问内存时过早释放内存。这还意味着您对内存泄漏有一些保障,当内存从代码或未使用的内存堆积中不可访问时发生。
在Unity中使用托管内存是管理应用程序内存的最简单方法;但它也有一些缺点。垃圾收集器很方便使用,但在释放和分配内存时的方式不可预测,这可能会导致性能问题,比如当垃圾收集器必须停止释放和分配内存时会发生卡顿。为了解决这种不可预测性,您可以使用C#非托管内存层。
有关托管内存工作原理的更多信息,请参阅托管内存文档。
C#非托管内存
C#非托管内存层允许您访问本地内存层以微调内存分配,同时编写C#代码非常方便。
您可以使用Unity核心API中的Unity.Collections命名空间(包括NativeArray)以及Unity Collections包中的数据结构来访问C#非托管内存。如果您使用Unity的C#作业系统或Burst,您必须使用C#非托管内存。有关更多信息,请参阅作业系统和Burst的文档。
本地内存
Unity引擎的内部C/C++核心具有自己的内存管理系统,称为本地内存。在大多数
情况下,您不能直接访问或修改这种内存类型。
Unity将您项目中的场景、资产、图形API、图形驱动程序、子系统和插件缓冲区以及本地内存内的分配都存储在本地内存中,这意味着您可以通过Unity的C# API间接访问本地内存。这意味着您可以安全且轻松地操作应用程序的数据,而不会失去Unity本地核心中的本地和高性能代码的好处。
大多数情况下,您不需要与Unity的本地内存交互,但每当您使用Profiler时,都可以通过Profiler标记来查看它如何影响应用程序的性能。
原文:Managed memory - Unity 手册
Managed memory
Unity的托管内存系统是基于Mono或IL2CPP虚拟机(VMs)的C#脚本环境。托管内存系统的好处在于它管理内存的释放,因此您不需要通过代码手动请求释放内存。
Unity的托管内存系统使用垃圾回收器和托管堆来自动释放内存分配,当您的脚本不再持有对这些分配的引用时。这有助于防止内存泄漏。内存泄漏发生在分配了内存后,对它的引用丢失,然后永远不会释放内存,因为它需要引用才能释放。
这个内存管理系统还保护内存访问,这意味着您不能访问已被释放的内存,或者对于您的代码来说从未有效的内存。然而,这个内存管理过程会影响运行时性能,因为分配托管内存对CPU来说是耗时的。垃圾回收也可能会在完成之前阻止CPU执行其他工作。
值类型和引用类型
当调用一个方法时,脚本后端会将其参数的值复制到为该特定调用保留的内存区域中,这个数据结构称为调用堆栈。脚本后端可以快速复制占用几个字节的数据类型。然而,对象、字符串和数组通常要大得多,而且脚本后端定期复制这些类型的数据是低效的。
在托管代码中,所有非空引用类型对象和所有装箱的值类型对象都必须分配在托管堆上。
熟悉值类型和引用类型对于有效管理代码很重要。有关更多信息,请参阅Microsoft的有关值类型和引用类型的文档。
自动内存管理
当创建对象时,Unity会从称为堆的中央池中分配所需的内存,这是您的Unity项目选择的脚本运行时(Mono或IL2CPP)自动管理的内存部分。当对象不再使用时,曾经占用的内存可以被回收并用于其他用途。
Unity的脚本后端使用垃圾回收器来自动管理应用程序的内存,因此您不需要使用显式方法调用来分配和释放这些内存块。自动内存管理需要的编码工作较少,并减少了内存泄漏的可能性。
托管堆概述
托管堆是您的Unity项目选择的脚本运行时(Mono或IL2CPP)自动管理的内存部分。
在上面的图表中,蓝色框表示Unity分配给托管堆的一定数量的内存。其中的白色框代表Unity在托管堆的内存空间内存储的数据值。当需要额外的数据值时,Unity会从托管堆(标有A的地方)分配它们的空间。
内存碎片和堆扩展
上图显示了内存碎片的示例。当Unity释放一个对象时,该对象占用的内存会被释放。然而,空闲空间不会成为一个“自由内存”的单一大池。
已释放对象的两侧可能仍在使用。因此,被释放的空间是其他内存段之间的“间隙”。Unity只能使用这个间隙来存储与已释放对象相同或更小尺寸的数据。
这种情况被称为内存碎片化。当堆中有大量可用内存,但只能在对象之间的“间隙”中使用时,就会发生这种情况。这意味着即使总共有足够的空间来分配大块内存,托管堆也找不到足够大的连续内存块来分配给该分配。
(被标注为A的对象是需要添加到堆上的新对象。被标注为B的项目是已释放对象占用的内存空间,以及空闲的未分配内存。尽管总的空闲空间足够,但由于没有足够的连续空间,所以被标注为A的新对象的内存无法适应堆,因此必须运行垃圾收集器。)
如果分配了一个大对象,并且没有足够的连续空闲空间来容纳它,如上所示,Unity内存管理器会执行两个操作:
首先,如果垃圾回收器尚未运行,则运行垃圾回收器。这试图释放足够的空间来满足分配请求。
如果在垃圾回收器运行后,仍然没有足够的连续空间来容纳请求的内存量,则必须扩展堆。堆扩展的具体量取决于平台,但在大多数平台上,当堆扩展时,它会扩展到前一次扩展的两倍。
托管堆扩展注意事项
堆意外扩展可能会引发问题。Unity的垃圾收集策略倾向于更频繁地碎片化内存。您应该注意以下事项:
Unity不会在定期扩展堆时释放分配给托管堆的内存;相反,它会保留扩展的堆,即使其中的大部分部分为空。这是为了防止需要重新扩展堆,如果发生更多的大型分配。
在大多数平台上,Unity最终会将托管堆的空闲部分使用的内存释放回操作系统。这种情况发生的间隔不受保证,不可靠。
相关文章:

Unity官方文档中关于内存管理的翻译(2021.3)
原文:Memory in Unity - Unity 手册 Unity内存管理 为了确保您的应用程序运行时没有性能问题,了解Unity如何使用和分配内存非常重要。本文档的这一部分解释了Unity中内存是如何工作的,适用于希望了解如何提高应用程序内存性能的读者。 Unity使用三个内…...

点云处理开发测试题目 完整解决方案
点云处理开发测试题目 文件夹中有一个场景的三块点云数据,单位mm。是一个桌子上放了一个纸箱,纸箱上有四个圆孔。需要做的内容是: 1. 绘制出最小外接立方体,得到纸箱的长宽高值。注意高度计算是纸箱平面到桌子平面的距离。 2. 计算出纸箱上的四个圆的圆心坐标和半径,对圆…...

TensorRT的结构
Builder(网络原数据):模型搭建的入口,网络的tensorRT内部表示以及可执行程序引擎都是由该对象的成员方法生成的 BuiderConfig(网络原数据的选项):负责设置模型的一些参数,如是否开始…...

python对excel数据表进行数据清洗
当拿到excel表,使用python对excel操作前,第一件事情是对excel表的数据进行数据清洗。 数值是否有空值,是否有重复的数据,把以上2个问题解决完成以后,才是对数据真正操作的开始。 1、使用pandans读取数据 2、判断exce…...

95、Spring Data Redis 之使用RedisTemplate 实现自定义查询 及 Spring Data Redis 的样本查询
Spring Data Redis 之使用RedisTemplate 实现自定义查询 Book实体类 原本的接口,再继承我们自定义的接口 自定义查询接口----CustomBookDao 实现类:CustomBookDaoImpl 1、自定义添加hash对象的方法 2、自定义查询价格高于某个点的Book对象 测试&a…...

jdbc(DriverManager+Connection+Statement+ResultSet)+SQL注入+开启预编译+数据连接池
1 JDBC概念 JDBC 就是使用Java连接并操作数据库的一套API 全称:( Java DataBase Connectivity ) Java 数据库连接 2 JDBC优势 可随时替换底层数据库,访问数据库的Java代码基本不变 以后编写操作数据库的代码只需要面向JDBC(接口…...

NoSQL之 Redis命令工具及常用命令
目录 1 Redis 命令工具 1.1 redis-cli 命令行工具 1.2 redis-benchmark 测试工具 2 Redis 数据库常用命令 2.1 set:存放数据,命令格式为 set key value 2.2 get:获取数据,命令格式为 get key 2.3 keys 命令可以取符合规则的…...

多线程(线程互斥)
抢票代码编写 学习了前面有关线程库的操作后,我们就可以模拟抢票的过程 假设我们创建四个线程,分别代表我们的用户 然后设定总票数为1000张,四个线程分别将进行循环抢票操作,其实就是循环对票数进行打印,并进行对应的…...

使用 html2canvas 和 jspdf 将页面转 pdf,同时解决当页面过长时,页面白屏问题
代码如下,直接粘贴复制即可,代码中 jspdf 是全局引入,你可以自己局部引入 别人使用标签的方式来显示 base64,但是当页面过长时,base64 大小过大会导致页面解析异常,显示白屏 import html2canvas from html2…...

【Python 千题 —— 基础篇】今年几岁啦
题目描述 题目描述 介绍自己的年龄。请使用 input 函数读入一个整数,表示自己的年龄,然后程序将自动生成介绍自己年龄的英文语句。 输入描述 输入一个整数,表示自己的年龄。 输出描述 程序将生成一个英文语句,以介绍自己的年…...

git push 失败 shallow update not allowed
问题描述: ~/OK62xx-linux-sdk/OK62xx-linux-kernel$ git push origin master Counting objects: 83919, done. Delta compression using up to 144 threads. Compressing objects: 100% (75697/75697), done. Writing objects: 100% (83919/83919), 232.41 MiB | …...

uniapp 在uni.scss 根据@mixin定义方法 、通过@include全局使用
在官方文档中提及到uni.scss中变量的使用,而我想定义方法,这样写css样式更方便 一、官方文档的介绍 根据官方文档我知道,在这面定义的变量全局都可使用。接下来我要在这里定义方法。 二、在uni.scss文件中定义方法 我在uni.scss文件中定义了…...

C++ 类和对象(一)
1.面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完 成。 …...

rust函数
一 、函数定义 (一)格式 使用fn关键字 fn是 function 的缩写 1.无返回值的 fn 函数名 ( [paraname: type]... ) {函数体 }参数必须声明参数名称和类型 函数定义时指定的参数叫做 形参。调用时传递给函数的参数叫做 实参。 例子 fn another_function(…...

链表的基本操作
(一)实验类型:设计性 (二)实验目的: 1. 掌握线性表的链式存贮结构及基本操作,深入了解链表的基本特性,以便在实际问题背景下灵活运用它们。 2. 巩固该存贮结构的构造方法࿰…...

Flutter AI五子棋
前言 在上一篇文章中,讲解了如何实现双人在本地对战的五子棋,但是只有一个人的时候就不太好玩,同时博主也没有把五子棋相关的文章写过瘾。那么这篇文章,我们来实现一个功能更加丰富的五子棋吧!在设计五子棋的算法方面&…...

springboot项目中后台文件上传处理
参考地址:http://www.gxcode.top/code 文件上次核心处理代码: @Autowired private FileUpload fileUpload; //获取资源对象:file-upload-prod.properties@ApiOperation(value = "用户头像修改", notes = "用户头像修改", httpMethod =...

【SQL】MySQL中的存储引擎、事务、锁、日志
存储引擎: 数据库管理系统(DBMS)使用数据存储引擎进行创建、查询、更新和删除数据。 MySQL5.5之前默认的存储引擎是MyISAM,5.5及之后版本默认的存储引擎是InnoDB。(my.ini中指定的) MyISAM:不支持事务,不支…...

DRM全解析 —— CRTC详解(2)
接前一篇文章:DRM全解析 —— CRTC详解(1) 本文继续对DRM中CRTC的核心结构struct drm_crtc的成员进行释义。 3. drm_crtc结构释义 (5)struct drm_modeset_lock mutex /*** @mutex:** This provides a read lock for the overall CRTC state (mode, dpms* state, ...) an…...

3d环形图开发(vue3+vite+ts)
开发效果(待完善): 技术支持: Echarts echarts-gl 安装: 注:echarts与echarts-gl版本需对应,可参考官网 pnpm add echarts4.9.0 echarts-gl1.1.2 组件封装: <template><…...

element ui中父子组件共用一个el-dialog弹窗,切换组件页面弹窗进行关闭
在Element UI中,如果多个父子组件共用一个el-dialog弹窗,并且需要在切换组件页面时关闭弹窗,你可以考虑以下方法来实现: 使用Vuex进行状态管理: 在Vuex中创建一个状态来管理弹窗的显示状态(例如࿰…...

基于Keil a51汇编 —— Segments, Modules, and Programs
段、模块和程序 在初始设计阶段,定义程序要执行的任务,然后划分为子程序。以下是与 Ax51 汇编器和 Lx51 链接器/定位器一起使用的子程序类型的简要介绍。 段是代码块或数据存储器。段可以是可重定位的,也可以是绝对的。可重定位段具有名称、…...

基于Java+SpringBoot+Vue民宿管理系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...

【Qt】三种方式实现抽奖小游戏
简介 本文章是基本Qt与C实现一个抽奖小游戏,用到的知识点在此前发布的几篇文章。 下面是跳转链接: 【Qt控件之QLabel】用法及技巧链接: https://blog.csdn.net/MrHHHHHH/article/details/133691441?spm1001.2014.3001.5501 【Qt控件之QPus…...

【算法与数据结构】--算法基础--算法设计与分析
一、贪心算法 贪心算法是一种解决优化问题的算法设计方法,其核心思想是在每一步选择当前状态下的最优解,从而希望最终达到全局最优解。下面将介绍贪心算法的原理、实现步骤,并提供C#和Java的实现示例。 1.1 原理: 贪心算法的原…...

vue部分入门知识点代码示例
1. Vue实例 Vue.js的核心是Vue实例,用来管理你的应用。以下是一个创建Vue实例的示例: <!DOCTYPE html> <html> <head><title>Vue.js入门示例</title><!-- 引入Vue.js库 --><script src"https://cdn.jsdel…...

【图灵】Spring为什么要用三级缓存解决循环依赖问题
这里写自定义目录标题 一、什么是循环依赖二、什么是单例池?什么是一级缓存?三、什么是二级缓存,它的作用是什么?四、什么是三级缓存,它的作用是什么?五、为什么Spring一定要使用三级缓存来解决循环依赖六、…...

bert入门
bert是什么 BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)中的预训练模型,它是基于Transformer架构的一种深度学习模型。BERT的主要目标是在大规模文本语料库上进行预训练&a…...

蓝桥杯基础---切面条
切面条 一根高筋拉面,中间切一刀,可以得到2根面条。 如果先对折1次,中间切一刀,可以得到3根面条。 如果连续对折2次,中间切一刀,可以得到5根面条。 那么,连续对折10次,中间切一刀…...

Spring Data Redis使用方式
1.导入Spring Data Redis的maven坐标 pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2. 配置Redis数据源 2.1application.yml文件…...