Unity UI内存泄漏优化
项目一运行,占用的内存越来越多,不会释放,导致GC越来越频繁,越来越慢,这些都是为什么呢,今天从UI方面谈起。
首先让我们来聊聊什么是内存泄漏呢?
一般来讲内存泄漏就是指我们的应用向内存申请了一块地址,然后这块地址的相关引用全部丢失了,这块内存无法再被分配,在计算机眼里,那就是丢了,找不回来了,除非重启。。。
不过,这里如果我们要去理解Unity中的内存泄漏,那我们首先要了解一下Unity的内存分配机制和GC机制,哇,不过说真的,要真是细说这两点,那真是几天都讲不完呀,还是算了,哈哈,这里大概聊一下,
程序在运行的时候,会先从计算机中申请一块内存,这时候如果我们需要去申请一块地址的时候,Unity会先去从堆内存中找合适大小的地址块给我们,但是这时候,如果堆内存用完了,这时候GC就出马了,会先清理一遍当前内存中无用的数据然后给我们分配所需要的内存块,那这个时候如果GC之后还是没有找到足够大小的内存给我们用怎么办呢,Unity只能去在申请一块之前内存2被大小的内存了。
这时候来想想,如果在我们的项目中这如果不断重复上述步骤,那么这时候是不是就意味着内存泄漏了呢。。。 现在就让我们开始从实际情况来一探究竟吧!!!
一开始我们通过Unity的Profiler工具只能看到在我们的UI已经关闭销毁了可是UI里面用到的图集还在内存里面存在,不应该呀,如果图集不释放,那岂不是意味着我们如果打开很多UI的时候,这些图集资源就要占到很多内存,如何查看当前内存中图集情况,可以参考下图,先选中Memory模块,然后选择Detailed,点击Take Sample Playmode,这时候内存中的图集就出现在下面了,参考5的位置,这里说明一下位置4这个选项,如果不勾选,进行内存采样速度会快很多,勾选了会慢很多,但是会同时采样出对应资源当前的引用情况。

这时候我们通过对游戏中不同节点进行内存采样,便能分析出我们哪些图集没有随着预设的销毁而销毁。
问题已经找到了,那么如何解决呢,如何下手呢,这时候又不知道怎么办了,害!!!

但是生活还要继续,问题还得解决呀,那么接下来就开始了问题分析,无数次Demo测试,从AB包加载卸载,到Unity内存分配管理,从GC的工作方式,到GC的底层实现原理,终于发现了这几个问题。
首先,如果我们的项目是通过AssetBundle方式加载的,那么在我们切场景或者进行阶段变化的时候我们需要处理一下无用资源的释放,调用一下下面的接口。
Resources.UnloadUnusedAssets();
卸载未使用的资源
这时候我们在进行内存对比分析的时候会发现会有一些内存被释放,可是图集不销毁的问题还在,害,还以为挺简单的,目前看来问题更复杂了。。。
这时候用上了另一个工具Memory Profiler,这个工具是在Unity2020之后的版本推出的功能,对当前内存进行快照,可视化的形式显示当前内存分配的大小,列出了每个托管对象的类型,值,占用大小,地址,被引用链等等信息,还可以进行快照对比,分析两次内存快照新增、删除和保持不变的内存对象,从而更方便快捷的定位项目内存的使用情况。
通过对内存进行快照,分析图集的引用链,屏蔽代码,重新快照测试,一次次的测试,慢慢缩小代码范围,定位图集不销毁的原因,最终发现原来是我们的UI使用了static实例来实现单例效果,在其他地方调用,但是在我们UI不需要的时候并没有将这个静态单例设置为null,导致整个UI资源的相关引用一直存在,无法释放,还有就是我们在对按钮进行事件注册的时候,使用了项目封装的接口,而项目封装的接口在拿到委托事件对象后,并没有在移除事件的时候去清除委托事件对象,导致引用一直存在,相关的资源也就无法释放。

相信经过上述步骤之后我们的图集不销毁问题已经解决了大部分了,具体还有哪些,后面有需要我们再补充,哈哈。
这里再说一个图片不销毁问题,在项目中我们经常会去动态替换某些图片来实现我们的功能,这时候有一个统一接口就很方便了,可是图片不销毁问题也正好跟这个动态替换接口有关,由于我们的统一接口会保存一份加载的图片的引用,在对应预设销毁的时候,由于图片引用一直存在,所以图片就无法被GC处理掉,这时候我们可以考虑对我们动态加载的图片进行场景管理,在合适的时候清空一次引用列表,还有由于我们动态图片加载是自己管理加载资源,所以我们在清空列表的时候要调用一次对应接口的卸载资源接口,否则,资源还是无法从内存中释放。
目前为止,图集图片不销毁问题已经解决了大部分,至于项目中具体还有没有其他问题导致,有待后续研究,,,总结一下:
- 使用了static静态类方式来实现单例的UI,在使用完之后一定记得将对应单例设置为null,让GC可以去释放对应的内存。
 - 在使用委托或者其他时候,拿到类对象的引用在使用完之后一定要记得释放引用。
 - 加载的资源在不适用的时候记得卸载掉,比如AssetBundle.Load()和AssetBundle.Unload()
 - 在适当的时机调用Resource.UnloadUnusedAssets()接口释放无用的资源
 
简而言之,言而简之,内存优化一直是项目开发中的重头戏,任重而道远呀。。。

心怀梦想 奔向远方
相关文章:
Unity UI内存泄漏优化
项目一运行,占用的内存越来越多,不会释放,导致GC越来越频繁,越来越慢,这些都是为什么呢,今天从UI方面谈起。 首先让我们来聊聊什么是内存泄漏呢? 一般来讲内存泄漏就是指我们的应用向内存申请…...
学习笔记:Opencv实现图像特征提取算法SIFT
2023.8.19 为了在暑假内实现深度学习的进阶学习,特意学习一下传统算法,分享学习心得,记录学习日常 SIFT的百科: SIFT Scale Invariant Feature Transform, 尺度不变特征转换 全网最详细SIFT算法原理实现_ssift算法_Tc.小浩的博客…...
【golang】接口类型(interface)使用和原理
接口类型的类型字面量与结构体类型的看起来有些相似,它们都用花括号包裹一些核心信息。只不过,结构体类型包裹的是它的字段声明,而接口类型包裹的是它的方法定义。 接口类型声明中的这些方法所代表的就是该接口的方法集合。一个接口的方法集…...
【Linux操作系统】Linux系统编程中的共享存储映射(mmap)
在Linux系统编程中,进程之间的通信是一项重要的任务。共享存储映射(mmap)是一种高效的进程通信方式,它允许多个进程共享同一个内存区域,从而实现数据的共享和通信。本文将介绍共享存储映射的概念、原理、使用方法和注意…...
2235.两整数相加:19种语言解法(力扣全解法)
【LetMeFly】2235.两整数相加:19种语言解法(力扣全解法) 力扣题目链接:https://leetcode.cn/problems/add-two-integers/ 给你两个整数 num1 和 num2,返回这两个整数的和。 示例 1: 输入:num…...
中国剩余定理及扩展
目录 中国剩余定理解释 中国剩余定理扩展——求解模数不互质情况下的线性方程组: 代码实现: 互质: 非互质: 中国剩余定理解释 在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二&#x…...
数据在内存中的存储(deeper)
数据在内存中的存储(deeper) 一.数据类型的详细介绍二.整形在内存中的存储三.浮点型在内存中的存储 一.数据类型的详细介绍 类型的意义: 使用这个类型开辟内存空间的大小(大小决定了使用范围)如何看待内存空间的视角…...
算法修炼Day52|● 300.最长递增子序列 ● 674. 最长连续递增序列 ● 718. 最长重复子数组
LeetCode:300.最长递增子序列 300. 最长递增子序列 - 力扣(LeetCode) 1.思路 dp[i]的状态表示以nums[i]为结尾的最长递增子序列的个数。 dp[i]有很多个,选择其中最大的dp[i]Math.max(dp[j]1,dp[i]) 2.代码实现 1class Solution {2 pub…...
使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器
使用 HTML、CSS 和 JavaScript 创建实时 Web 编辑器 在本文中,我们将创建一个实时网页编辑器。这是一个 Web 应用程序,允许我们在网页上编写 HTML、CSS 和 JavaScript 代码并实时查看结果。这是学习 Web 开发和测试代码片段的绝佳工具。我们将使用ifram…...
百望云联合华为发布票财税链一体化数智解决方案 赋能企业数字化升级
随着数据跃升为数字经济关键生产要素,数据安全成为整个数字化建设的重中之重。为更好地帮助企业发展,中央及全国和地方政府相继出台了多部与数据相关的政策法规,鼓励各领域服务商提供具有自主创新的软件产品与服务,帮助企业在合规…...
实现两个栈模拟队列
实现两个栈模拟队列 思路:可以想象一下左手和右手,两个栈:stack1(数据所在的栈) ,stack2(临时存放)。 入队:需要将入队 num 加在 stack1 的栈顶即可; 出队&am…...
无涯教程-TensorFlow - 单词嵌入
Word embedding是从离散对象(如单词)映射到向量和实数的概念,可将离散的输入对象有效地转换为有用的向量。 Word embedding的输入如下所示: blue: (0.01359, 0.00075997, 0.24608, ..., -0.2524, 1.0048, 0.06259) blues: (0.01396, 0.11887, -0.48963, ..., 0.03…...
Facebook AI mBART:巴别塔的硅解
2018年,谷歌发布了BERT(来自transformers的双向编码器表示),这是一种预训练的语言模型,在一系列自然语言处理(NLP)任务中对SOTA结果进行评分,并彻底改变了研究领域。类似的基于变压器…...
BDA初级分析——SQL清洗和整理数据
一、数据处理 数据处理之类型转换 字符格式与数值格式存储的数据,同样是进行大小排序, 会有什么区别? 以rev为例,看看字符格式与数值格式存储时,排序会有什么区别? 用cast as转换为字符后进行排序 SEL…...
汽车后视镜反射率测定仪
后视镜是驾驶员坐在驾驶室座位上直接获取汽车后方、侧方和下方等外部信息的工具。它起着“第三只眼睛”的作用。后视镜按安装位置划分通常分为车外后视镜、监视镜和内后视镜。外后视镜观察汽车后侧方监视镜观察汽车前下方内后视镜观察汽车后方及车内情况。用途不一样镜面结构也…...
Redis学习笔记
redis相关内容 默认端口6379 默认16个数据库,初始默认使用0号库 使用select 切换数据库 统一密码管理,所有库密码相同 dbsize:查看当前库key的数量 flushdb:清空当前库 flushall:清空全部库 redis是单线程 多路…...
韩顺平Linux 四十四--
四十四、rwx权限 权限的基本介绍 输入指令 ls -l 显示的内容如下 -rwxrw-r-- 1 root 1213 Feb 2 09:39 abc0-9位说明 第0位确定文件类型(d , - , l , c , b) l 是链接,相当于 windows 的快捷方式- 代表是文件是普通文件d 是目录,相…...
【支付宝小程序】分包优化教程
🦖我是Sam9029,一个前端 Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 🐱🐉🐱🐉恭喜你,若此文你认为写的不错,不要吝啬你的赞扬,求收…...
语言基础2 矩阵和数组
语言基础2 矩阵和数组 矩阵和数组是matlab中信息和数据的基本表示形式 可以创建常用的数组和网格 合并现有的数组 操作数组的形状和内容 以及使用索引访问数组元素 用到的函数列表如下 一 创建 串联和扩展矩阵 矩阵时按行和列排列的数据元素的二维数据元素的二维矩…...
springMVC中过滤器抛出异常,自定义异常捕获
在过滤器中引入org.springframework.web.servlet.HandlerExceptionResolver AutowiredQualifier("handlerExceptionResolver")private HandlerExceptionResolver resolver; // doFilter中处理if (条件1) {if (条件2) {resolver.resolveException(request, response, …...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
