unityprofiler
2020-03-30 14:49 kk20161206 阅读(197) 评论(0) 编辑 收藏 举报unity优化之profile数据:https://www.muchenhen.com/2019/03/21/%E3%80%90unity%E4%BC%98%E5%8C%96%E3%80%91profiler%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90%E5%99%A8/
Unity内存管理机制
(1)堆内存和堆栈内存两种内存管理池,堆栈存较小和短暂的数据,堆存较大、存储时间较长的数据。
(2)unity中的变量只会在堆栈或者堆内存上进行内存分配
(3)只要变量处于激活状态,则其占用的内存会被标记为使用状态,则该部分的内存处于被分配的状态
(4)一旦变量不再激活,则其所占用的内存不再需要,该部分内存可以被回收到内存池中再次被使用,这样的操作就是内存回收。处于堆栈上的内存回收速度极其快速,处于堆上的内存并不是及时回收的,因此其对应的内存依然会被标记为使用状态。
(5)垃圾回收主要是指堆上的内存分配和回收,unity中会定时堆堆内存进行GC操作
堆栈上的内存分配和回收十分快捷简单,因为堆栈上只会存储短暂的或者较小的变量。内存分配和回收都会一种顺序和大小都可控制的形式进行。 堆栈的运行方式就像数据结构的stack一样,本着先进后出的原则
(1)首先,unity检测是否有足够的闲置内存单元用来存储数据,如果有,则分配对应大小的内存单元;
(2)如果没有足够的内存单元,unity会触发来及回收来释放不再被使用的堆内存。这步操作是一步缓慢的操作,如果垃圾回收后有足够大小的内存单元则进行内存分配;
(3)如果垃圾回收后并没有足够的内存单元,则unity会扩展堆内存的大小,这种操作会很缓慢,然后分配对应大小的内存单元给变量;
(1)GC会检查堆内存上的每个存储变量; (2)对每个变量会检测其引用是否处于激活状态; (3)如果变量的引用不再处于激活状态,则会被标记为可回收; (4)被标记的变量会被移除,其所占有的内存会被回收给堆内存。
主要有三个操作会触发垃圾回收: (1)在堆内存上进行内存分配操作而内存不够的时候; (2)GC会自动的触发,不同的平台运行频率不一样; (3)GC可以被强制执行。
void ExampleFunc() { int localInt =5; }
void ExampleFunc() { List localList = new List(); }
(1)减少GC运行的次数; (2)减少单次GC的运行时间; (3)将GC的运行时间延迟,避免在关键时刻触发,如在加载场景是调用GC
(1)对游戏进行重构,减少堆内存的分配和引用的分配。
更少的变量和引用会减少GC操作中的检测个数
从而提高GC运行的效率
(2)降低堆内存分配和回收的频率,尤其是在关键时刻。
也就是说更少的事件触发GC操作,同时也降低堆内存的碎片化
(3)我们可以试着测量GC和堆内存扩展的时间,使其按照可预测的顺序执行。
当然这样操作的难度极大,但是这会大大降低GC的影响
void OnTriggerEnter(Collider other) { Renderer[] allRenderers = FindObjectsOfType<Renderer>(); ExampleFunction(allRenderers); }
private Renderer[] allRenderers; void Start() { allRenderers = FindObjectsOfType<Renderer>(); } void OnTriggerEnter(Collider other) { ExampleFunction(allRenderers); }
private float timeSinceLastCalled; private float interval; void Update() { timeSinceLastCalled +=Time.deltaTime; if(timeSinceLastCalled>interval) { ExampleGarbageGenerationFunction(); timeSinceLastCalled = 0f; } }
void Update() { List myList = new List(); PopulateList(myList); }
private List myList = new List(); void Update() { myList.Clear(); PopulateList(myList); }
public Text timerTxt; private float timer; void Update() { timer +=Time.deltaTime; timerTxt.text = "Time:" + timer.ToString(); }
public Text timerHeaderTxt; public Text timerValueTxt; private float timer; void Start() { timerHeaderText.text = "TIME:"; } void Update() { timerValueTxt.text = timer.ToString(); }
void ExampleFunction() { for(int i=0;i<myMesh.normals.Length;i++) { Vector3 normal = myMesh.normals[i]; } }
void ExampleFunction() { Vector3[] meshNormals = myMesh.normals; for(int i =0;i<meshNormals.Length;i++) { Vector3 normal = meshNormals[i]; } }
private string playerTag = "Player"; void OnTriggerEnter(Collider other) { bool isPlayer = other.gameObject.tag == playerTag; }
private string playerTag = "Player"; void OnTriggerEnter(Collider other) { bool isPlayer = other.gameObject.Compare(playerTag); }
void ExampleFunction() { int cost = 5; string displayString = Srting.Format("Price:{0} glod",cost); }
yield return 0;
yield return null;
while(!complete) { yield return new WaitForSeconds(1f); }
WaitForSeconds delay = new WaitForSeconds(1f); while(!insComplete) { yield return delay; }
void ExampleFunction(List listOfInts) { foreach(int currentInt in listOfInts) { DoSomething(currentInt); } }
void ExampleFunction(List listOfInts) { for(int i = 0;i< listOfInts.Count;i++) { int currentIn = listOfInts[i]; DoSometing(currentInt); } }
public struct ItemData { public string name; public int cost; public Vector3 position; } private ItemData[] itemData;
private string[] itemNames; private int[] itemCosts; private Vector3[] itemPositions;
public class DialogData { private DialogData nextDialog; public DialogData GetNextDialog() { return nextDialog; } }
public class DialogData { private int nextDialogID; public int GetNextDialogID() { return nextDialogID; } }
System.GC.Collect()
10. 枚举和字典
Dictionary.add(key, value)
会调用 Object.getHashCode(Object),Object.getHashCode是引用类型,enum是值类型,以enum为key的字典会导致装箱操作。所以,最好实现个IEqualityComparer接口,该接口的实现传给dictionary的构造函数,dictionary的比较。
public class MyEnumComparer : IEqualityComparer<MyEnum> { public bool Equals(MyEnum x, MyEnum y) { return x == y; } public int GetHashCode(MyEnum x) { return (int)x; } }
11. Empty array reuse
Some teams prefer to return empty arrays instead of null
when an array-valued method needs to return an empty set. This coding pattern is common in many managed languages, particularly C# and Java.
In general, when returning a zero-length array from a method, it is considerably more efficient to return a pre-allocated singleton instance of the zero-length array than to repeatedly create empty arrays(5) (Note: Naturally, an exception should be made when the array is resized after being returned)
12. string的优化:
String.Equals("encyclopedia", “encyclopædia”); myString.Equals(otherString, StringComparison.Ordinal);
Inefficient built-in string APIs
String.StartsWith
and String.EndsWith
can be replaced with simple hand-coded versions
Regular Expressions
Regex.Match(myString, "foo");
改为:
var myRegExp = new Regex("foo"); myRegExp.Match(myString);
unity允许自己的引擎代码和我们的脚本代码方式不同。他自己的引擎代码用手动内存管理进行管理。核心引擎代码要显示写内存怎么用。手动内存管理不用gc。
允许我们的代码用自动内存管理。
自动内存管理: 堆栈,堆(托管堆),短期、长期数据。
https://gameinstitute.qq.com/community/detail/125200
https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity4-1.html
https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity5.html
https://learn.unity.com/tutorial/fixing-performance-problems#5c7f8528edbc2a002053b595