UNITY性能优化☀️一、GC介绍与Unity内存管理方法

简介: UNITY性能优化☀️一、GC介绍与Unity内存管理方法


🟥 GC是Unity管理内存的一个方法

游戏运行时使用内存来存储数据,当这些数据不再被使用时,存储这些数据的内存被释放以便于之后这些内存可以被复用。

垃圾(Garbage )是存储无用数据的内存的术语,GC(Garbage Collection 垃圾回收)是使这些内存可以再次使用的过程。

GC是Unity对内存管理的方式之一,我们的游戏可能因为GC负担过重而表现不佳,比如卡顿、掉帧。所以GC是引起性能问题的一个常见原因。

在这篇文章中,我们将介绍GC的工作原理,和在什么情况下会触发GC、如何减少GC对游戏的影响。



🟧 频繁GC会造成帧率过低

GC引起的性能问题可表现为帧率过低,帧率剧烈波动或者间歇性卡顿。

但是其他问题也可能引起类似的症状。

如果你的游戏有这些性能问题,首先需要使用Unity的Profiler工具来确定这些问题是由GC引起的。

如何使用Profiler工具来确定引起性能问题的原因,可以查看 这篇教程:传送门



🟨 GC回收的原理

1️⃣ GC是个费时的操作

当堆变量超出作用域后,存储该变量的内存并没有被立即释放。无用的堆内存只在执行GC时被释放。



每次执行GC时, 将执行以下步骤:

  • 垃圾收集器检索堆上的每个对象。
  • 垃圾收集器搜索所有当前对象引用以确定堆上的对象是否仍在作用域内。
    不在作用域内的对象被标记为删除。
  • 删除被标记的对象并将内存返回给堆。

GC是个费时的操作,堆上的对象越多,代码中的引用数越多,GC就越费时。


2️⃣ 可能触发GC的三种情况

  • 堆分配时堆上的可用内存不足时触发GC。
  • GC会不时的自动运行(频率因平台而异)。
  • 手动强制调用GC

GC可能被频繁触发。每当无法从可用堆内存中实现堆分配时,就会触发GC,这意味着频繁的堆分配和释放可能导致GC频繁。


3️⃣ GC导致的问题

现在我们了解了GC在Unity内存管理中的作用,我们可以考虑可能发生的问题类型。


🚨GC可能花费相当长的时间来运行

这是最明显的问题。如果堆上有很多对象和大量的对象引用要检查,则检查所有这些对象的过程可能很慢。 这可能会导致我们的游戏卡顿或运行缓慢。


🚨GC可能在不合时宜的时刻被触发

如果CPU在我们游戏的性能关键部分已经满负荷了,那此时即使是少量的GC额外开销也可能导致我们的帧速率下降和性能问题。


🚨堆碎片

当从堆中分配内存时,会根据必须存储的数据大小从不同大小的块中的可用空间中获取内存。当这些内存块返回到堆时,堆可能分成很多由分配块分隔的小空闲块。这意味着虽然可用内存总量可能很高,但由于碎片化太过严重而无法分配一块连续的大内存块。导致GC被触发或不得不扩大堆大小。


堆内存碎片化有两个后果:

  • 一是游戏内存大小会远高于实际所需要的大小
  • 二是GC会被更频繁的触发。


🟩 Unity是怎样进行的内存管理

首先,我们要知道,Unity在运行自己的核心引擎代码,和运行我们写的代码时,内存管理使用了不同的方法。

  • 当Unity在运行自己的引擎代码时,使用手动内存管理。手动内存管理不使用GC,本文不做介绍。
  • 当Unity运行我们写的脚本时,使用自动内存管理,Unity会自动帮我们完成GC工作。


基本上来说,Unity自动内存管理这样工作:

Unity可以访问两个内存池:栈和堆(也称为托管堆)。栈用于短期存储小块数据,堆用于长期存储和较大数据段。

当创建变量时,Unity从栈或堆中申请内存,只要变量在作用域内,分配给它的内存就会一直在使用, 我们称这部分内存已被分配。

我们将栈中的变量称为栈对象,将堆中的变量称为堆对象。

当变量超出作用域时,该内存会不会再被使用,并且可以归还给原来的内存池。当内存归还给原有的内存池时,我们称该内存被释放。

栈内存在变量超出作用域时被实时释放,而堆内存在变量超出作用域之后并没有被释放并保持被分配的状态。

垃圾收集器(garbage collector)识别和释放未使用的堆内存。 垃圾收集器定期运行以清理堆。


🟦 栈和堆在内存管理上的不同

1️⃣ 在栈分配和释放时发生了什么

栈分配和释放简单快速。这是因为栈只用于在短时间内存储小数据。 分配和释放总是以可预测的顺序发生,并且具有可预测的大小。

栈的工作方式类似于栈数据类型, 它是一个简单的元素集合。

这种情况下的内存块,只能以严格的顺序添加和删除元素。 这种简单性和严格性使得它变得非常快速。

当一个变量存储在栈上时,它的内存就是简单地从栈顶分配。 栈变量超出作用域时,用于存储该变量的内存将立即返回栈进行重用。


2️⃣ 在堆分配时发生了什么

堆分配比栈分配复杂的多。因为:

  • 堆可以用来存储长期和短期数据
  • 堆可以存储不同类型、不同大小的数据
  • 堆分配和释放也并不总是按可预测的顺序进行
  • 堆且可能需要大小差距巨大的内存块

当一个堆变量创建时,将执行以下步骤:

1、检查堆上是否有足够的空闲内存

如果有,则分配该变量的内存。如果没有,Unity触发GC试图释放未使用的堆内存。

这个操作可能很慢。如果GC之后堆内存足够,则该变量的内存被分配。


2、如果GC之后堆上还是没有足够的空闲内存

Unity将向操作系统申请更多内存以扩大堆大小。这个操作可能很慢。

之后该变量的内存被分配。

堆分配可能会很慢,特别在必须执行GC和扩大堆大小时。

相关文章
|
2月前
|
语音技术 开发工具 图形学
Unity与IOS⭐一、百度语音IOS版Demo调试方法
Unity与IOS⭐一、百度语音IOS版Demo调试方法
|
13天前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
2月前
|
存储 监控 算法
Java内存管理:从垃圾收集到性能优化
【8月更文挑战第4天】在Java的世界中,内存管理是一块神秘的领域,它不仅关乎程序的稳定运行,更直接影响到系统的性能表现。本文将带你深入理解Java的垃圾收集机制,探讨如何通过合理的内存管理策略来提升应用效率。我们将一起分析JVM内存结构,探索不同的垃圾收集算法,并借助实际代码示例,学习如何监控和调优内存使用,以期达到减少延迟、防止内存泄漏的目的。
|
3月前
|
存储 监控 安全
内存卡数据恢复,3个方法帮你找回丢失的照片和视频
今天,针对内存卡数据恢复,本期做一个详细的归纳,分析常见的数据丢失原因、详细的数据恢复步骤、以及如何保护内存卡数据。
内存卡数据恢复,3个方法帮你找回丢失的照片和视频
|
2月前
|
前端开发 图形学
Unity精华☀️UI和物体可见性的判断方法
Unity精华☀️UI和物体可见性的判断方法
|
1月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
111 0
|
2月前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
42 0
|
3月前
|
运维 Java Linux
(九)JVM成神路之性能调优、GC调试、各内存区、Linux参数大全及实用小技巧
本章节主要用于补齐之前GC篇章以及JVM运行时数据区的一些JVM参数,更多的作用也可以看作是JVM的参数列表大全。对于开发者而言,能够控制JVM的部分也就只有启动参数了,同时,对于JVM的性能调优而言,JVM的参数也是基础。
|
3月前
|
监控 Java
JVM内存问题之使用jstat命令查看GC堆百分比占比情况,应该使用哪个选项
JVM内存问题之使用jstat命令查看GC堆百分比占比情况,应该使用哪个选项
|
2月前
|
C# 图形学 数据安全/隐私保护
Unity数据加密☀️反射的用法:变量、属性、方法、重载,反射在DLL中的使用方法
Unity数据加密☀️反射的用法:变量、属性、方法、重载,反射在DLL中的使用方法