一文读懂Unity性能分析工具:MemoryProfiler,教你轻松分析项目性能瓶颈

在这里插入图片描述

轻松掌握MemoryProfiler

      • MemoryProfiler的基本概念
      • 如何获取MemoryProfile
      • MemoryProfiler内存分析实践
        • 正常GC,合理释放对象的引用
        • 无法正常GC,对象引用没有合理释放。
      • 总结

MemoryProfiler的基本概念

Unity 性能分析器 (Unity Profiler)
是一种可以用来获取应用程序性能信息的工具。可以将性能分析器连接到网络中的设备或连接到已连接到计算机的设备,从而测试应用程序在目标发布平台上的运行情况。还可以在
Editor 中运行性能分析器,从而在开发应用程序时概要了解资源分配情况。

有关Unity中的MemoryProfiler工具的详细介绍,可以参考这个链接:性能分析器概述

总之:MemoryProfiler是一个可以用于在Unity的Editor模式和Runtime模式下检测应用的内存使用情况的工具,它是一个免费的Unity Package。

如何获取MemoryProfile

本文内容以Unity 2021.3.8f1c1版本为教学版本。(提供PackageManager的unity版本均适用)
1.使用Unity 2021.3.8f1c1版本打开或者新建任意一个需要进行Memory分析的项目。
在这里插入图片描述
2.通过编辑器顶部菜单栏打开PackageManager
在这里插入图片描述
3.安装MemoryProfiler包

点击PackageManager面板左上角的加号,在弹出的菜单项中选择第四个:Add package by name
在这里插入图片描述
在弹出的输入框内输入包名: com.unity.memoryprofiler。并点击输入框右侧的“Add”按钮,开始安装memoryprofiler
在这里插入图片描述
等待包安装结束后,就能在包列表中看到刚刚安装的Memory Profiler。
在这里插入图片描述

MemoryProfiler内存分析实践

这里我们通过两个示例来说明如何通过MemoryProfiler分析项目的内存情况, 找到内存泄漏的问题。
其中第一个示例演示正常GC后的内存情况,第二个示例演示对象引用没有正常释放导致无法GC的情况。

正常GC,合理释放对象的引用

示例脚本CorrectGC.cs内容:

1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using UnityEngine;
5 
6 public class CorrectGC : MonoBehaviour
7 {
8     private class ManagedObjectTest
9     {
10         public void TestFunc()
11         {
12             Debug.Log("TestFunc!!!");
13         }
14     }
15 
16     private Action callBack = null;
17     private ManagedObjectTest objTest = null;
18 
19     private void CreateManagedObject()
20     {
21         objTest = new ManagedObjectTest();
22         callBack = objTest.TestFunc;
23     }
24 
25     private void DestroyManagedObject()
26     {
27         if(objTest != null)
28         {
29             objTest = null;
30             callBack = null;
31             GC.Collect();
32         }
33     }
34 
35     // Start is called before the first frame update
36     void Start()
37     {
38         CreateManagedObject();
39     }
40 
41     // Update is called once per frame
42     void Update()
43     {
44         if (Input.GetKeyDown(KeyCode.R))
45         {
46             DestroyManagedObject();
47         }
48     }
49 }

从上面的代码可以看到,我们定义一个类ManagedObjectTest用来模拟实际业务中创建的对象。
在Start中通过CreateManagedObject方法创建一个ManagedObjectTest对象。
在Update中通过检测按键R的按下来释放之前创建的ManagedObjectTest对象。
首先将此脚本挂载到场景中任意一个活动的GameObject对象上。
在这里插入图片描述

运行此场景,然后打开MemoryProfiler工具
在这里插入图片描述
在这里插入图片描述
此时点击Memory Profiler面板中的“Capture New Snapshot”按钮,创建一个当前状态下的内存快照。
在这里插入图片描述
选中创建好的内存快照
在这里插入图片描述
切换到“Objects and Allocations”面板
在这里插入图片描述
我们通过类型名称来验证是否创建了我们代码中编写的ManagedObjectTest对象。
在这里插入图片描述
该对象的内存分析:
在这里插入图片描述

从上图可以看出,确实创建了一个ManagedObjectTest对象,并且从右侧的References面板可以看到这个对象一共有两个引用,其中一个是脚本中的ManagedObjectTest变量,另一个是Action类型的对象,它引用的是ManagedObjectTest对象中的TestFunc函数,所以也间接引用了此对象。
此时,我们切换到正在运行的Unity场景中,按下按键R,然后再次创建一个内存快照。
在这里插入图片描述

在这里插入图片描述

选中新创建的内存快照。继续切换到“Objects and Allocations”面板,用同样的方法查找看看ManagedObjectTest对象是否被释放掉了。
在这里插入图片描述

通过上图我们可以看到,对象已经被成功释放掉了。

无法正常GC,对象引用没有合理释放。

这次,我们将上面的脚本代码做一个更改:去掉DestroyManagedObject方法中的callBack=null;这行。
示例代码:

1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using UnityEngine;
5 
6 public class CorrectGC : MonoBehaviour
7 {
8     private class ManagedObjectTest
9     {
10         public void TestFunc()
11         {
12             Debug.Log("TestFunc!!!");
13         }
14     }
15 
16     private Action callBack = null;
17     private ManagedObjectTest objTest = null;
18 
19     private void CreateManagedObject()
20     {
21         objTest = new ManagedObjectTest();
22         callBack = objTest.TestFunc;
23     }
24 
25     private void DestroyManagedObject()
26     {
27         if(objTest != null)
28         {
29             objTest = null;
30             GC.Collect();
31         }
32     }
33 
34     // Start is called before the first frame update
35     void Start()
36     {
37         CreateManagedObject();
38     }
39 
40     // Update is called once per frame
41     void Update()
42     {
43         if (Input.GetKeyDown(KeyCode.R))
44         {
45             DestroyManagedObject();
46         }
47     }
48 }

这次我们在编辑器运行之后直接按下按键R,并创建一个新的内存快照。
在这里插入图片描述
此时我们发现,ManagedObjectTest对象并没有被正常释放掉,原因就在于我们删掉的那一行代码。从右侧的References面板中也可以看出来,ManagedObjectTest对象的引用少了一个,但是由于脚本中还有callBack对象引用着ManagedObjectTest对象的TestFunc方法,所以导致该对象没办法被GC(仅仅是引用计数少了一次,但是此时引用计数不为0,无法GC)。

总结

通过以上例子我们可以得出结论:C#中的GC会根据对象的引用计数来进行内存回收,当对象的引用计数为0的时候,对象的内存能过够被正常释放,否则不能。其中有关GC的一些细节可以参考此链接:垃圾回收的基本原理

分析上面我们举的两个例子,为什么第二个例子中ManagedObjectTest对象没办法被GC呢,因为我们CreateManagedObject方法中创建完对象之后,将对象的成员方法绑定到了脚本中的callBack对象。callBack对象的生命周期是高于ManagedObjectTest对象中的TestFunc方法的,这个赋值操作就导致我们提升了ManagedObjectTest对象的TestFunc方法的生命周期,使它可能高于了ManagedObjectTest对象本身。所以当我们除了给脚本中的objTest对象置为null之外忘记了同时给callBack置为null,就会导致ManagedObjectTest对象的TestFunc此时还是被callBack引用,这样就导致ManagedObjectTest对象无法被正常GC。现实情况中,如果我们没有仔细分析对象的生命周期,没有注意这种隐含的提升对象生命周期的代码逻辑,很有可能会在项目中产生对象的内存泄漏。

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

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

相关文章

C语言结构体详解

前言: 何为结构体,结构体又是什么呢,相信有很多小伙伴对结构体还没有一个清楚的概念,今天咱也一起来探讨一下何为结构体,在C语言当中有着许多的数据类型,如char,int,long&#xff0c…

成都欣丰洪泰文化传媒有限公司电商服务的行业先锋

在当今电商行业风起云涌的时代,成都欣丰洪泰文化传媒有限公司凭借其深厚的行业积淀和敏锐的市场洞察力,已经崭露头角,成为众多电商企业争相合作的对象。欣丰洪泰不仅专注于电商服务,更是以其专业的服务理念和创新的营销策略&#…

软考102-上午题-【信息安全】-杂题+小结

一、杂题 真题1: 真题2: 真题3: 真题4: 真题5: 真题6:

【JavaEE】Thread类中run和start的区别

文章目录 先说结论Run方法Start方法 先说结论 当你想要创建一个新的线程并执行某些任务时,你应该重写run方法以提供任务的具体实现,并通过调用start方法来启动新线程 run方法包含了线程应该执行的代码,但直接调用它并不会启动新的线程。 s…

yolov5目标检测可视化界面pyside6源码(无登录版)

这个是yolov5pyside6实现目标检测可视化的代码,本套项目没有用户登录的功能,如需用户登录版,看另一篇文章:yolov5pyside6登录用户管理目标检测可视化源码_yolov5用户登入功能-CSDN博客

java入门学习Day02

本文介绍的内容主要有:java的注释(样式)、关键字、字面量。 一、java中的注释 1、基本语法 ① 单行注释 //注释信息 ② 多行注释 /* 注释信息1 注释信息2, */ ③ 文档注释 /** 注释信息1 注释信息2, */ public class…

vue3封装Element分页

配置当前页 配置每页条数 页面改变、每页条数改变都触发回调 封装分页 Pagination.vue <template><el-paginationbackgroundv-bind"$attrs":page-sizes"pageSizes"v-model:current-page"page"v-model:page-size"pageSize":t…

C#预处理器指令(巨细版)

文章目录 一、预处理器指令的基本概念二、预处理器指令的基本规则三、C# 预处理器指令详解3.1 #define 和 #undef3.2 #if、#else、#elif 和 #endif3.3 #line3.4 #error 和 #warning3.5 #region 和 #endregion 四、高级应用&#xff1a;预处理器指令的最佳实践4.1 条件编译的最佳…

量化交易软件开发定制的步骤

量化交易软件的定制开发是一个复杂而精细的过程&#xff0c;需要经过一系列步骤来确保最终交付的软件符合客户的需求并具有高度的可靠性和效率。以下是量化交易软件开发定制的主要步骤&#xff1a; 1. 需求分析与规划 在开始开发之前&#xff0c;首先需要与客户深入沟通&…

k8s安装traefik作为ingress

一、先来介绍下Ingress Ingress 这个东西是 1.2 后才出现的&#xff0c;通过 Ingress 用户可以实现使用 nginx 等开源的反向代理负载均衡器实现对外暴露服务&#xff0c;以下详细说一下 Ingress&#xff0c;毕竟 traefik 用的就是 Ingress 使用 Ingress 时一般会有三个组件: …

AI新工具 小模型也有大智慧Qwen1.5-MoE;大模型动态排行榜;马斯克更新Grok-1.5

✨ 1: Qwen1.5-MoE 阿里巴巴一款小型 MoE 模型&#xff0c;只有 27 亿个激活参数&#xff0c;但性能与最先进的 7B 模型&#xff08;如 Mistral 7B 和 Qwen1.5-7B&#xff09;相匹配。 Qwen1.5-MoE是一个使用混合专家模型&#xff08;Mixture-of-Experts&#xff0c;MoE&…

每日一练 两数相加问题(leetcode)

原题如下&#xff1a; 这道题目是一道链表题&#xff0c;我们对于这种链表类&#xff0c;很显然我们最后输出的是初始节点&#xff0c;所以我们要保留我们的初始头指针&#xff0c;那么我们的第一步一定是把头指针保留一份&#xff0c;然后再让头指针往后进行操作。那么我们进行…