测试一:使用member function创建action会产生gc,不管该函数是否访问外部变量:
private System.Action memberAct = null; // gc 112B private void ActionWithMethod() { memberAct = new System.Action(LocalMethod); } // gc 112B, if LocalMethod() is static, then no gc private void ActionWithMethod2() { memberAct = LocalMethod; } // no gc private void ActionWithMethod3() { System.Action act = memberAct; } private void LocalMethod() { foreach (var item in lst) Debug.Log(item); }
ActionWithMethod和ActionWithMethod2是等效的,gc值如下所示:
IL代码也一摸一样:
所以将一个member function复制给一个action会产生gc,解决的办法就是ActionWithMethod3,也就是用一个actin member缓存起来,然后将缓存的action member复制给新建的action,这样只会产生一次gc:
如果将LocalMethod设置为static函数,则ActionWithMethod2也不会产生gc:
private static void LocalMethod() { }
测试二:使用匿名函数,如果访问了外部变量,也会产生gc;如果不访问外部变量,则只产生一次gc
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TestAnonymousFunctionGC : MonoBehaviour { private System.Action actMember = null; private int iMember = 1; public TestAnonymousFunctionGC() { } private void Update() { UnityEngine.Profiling.Profiler.BeginSample("*** AnoymousFunctionWithoutArg ***"); AnoymousFunctionWithoutArg(); UnityEngine.Profiling.Profiler.EndSample();
UnityEngine.Profiling.Profiler.BeginSample("*** AnoymousFunctionWithMemberArg ***"); AnoymousFunctionWithMemberArg(); UnityEngine.Profiling.Profiler.EndSample();
UnityEngine.Profiling.Profiler.BeginSample("*** AnoymousFunctionWithLocalArg1 ***"); AnoymousFunctionWithLocalArg1(); UnityEngine.Profiling.Profiler.EndSample();
UnityEngine.Profiling.Profiler.BeginSample("*** AnoymousFunctionWithLocalArg2 ***"); AnoymousFunctionWithLocalArg2(); UnityEngine.Profiling.Profiler.EndSample();
UnityEngine.Profiling.Profiler.BeginSample("*** AnoymousFunctionWithLocalArg3 ***"); AnoymousFunctionWithLocalArg3(); UnityEngine.Profiling.Profiler.EndSample(); } // no gc private void AnoymousFunctionWithoutArg() { actMember = () => { }; } // gc 112B private void AnoymousFunctionWithMemberArg() { actMember = () => { Debug.Log(iMember); }; } // gc 129B private void AnoymousFunctionWithLocalArg1() { bool bValue = true; actMember = () => { Debug.Log(bValue); }; } // gc 132B private void AnoymousFunctionWithLocalArg2() { int iValue = 100; actMember = () => { Debug.Log(iValue); }; } // gc 136B private void AnoymousFunctionWithLocalArg3() { int iValue = 1; int iValue2 = 2; actMember = () => { Debug.Log(iValue); Debug.Log(iValue2); }; } }
同时还可以发现,匿名函数引用的外部变量的个数会影响gc的值,为什么呢?来分析一波:
可以看到访问外部变量的匿名函数,会导致临时对象的创建,这样会导致gc,那位为什么每个临时变量的gc值不一样呢,我们来看一下这些临时class的定义:
可以看匿名函数所访问的外部变量都会在临时类里面创建一个拷贝,这样每个类对象的大小就不一样了。
附上类型定义的完整代码,前因后果一目了然:
public class TestAnonymousFunctionGC : MonoBehaviour { // Fields private Action actMember; private int iMember; // Methods public TestAnonymousFunctionGC() { this.actMember = null; this.iMember = 1; base..ctor(); return; } [CompilerGenerated] private void <AnoymousFunctionWithMemberArg>b__5_0() { Debug.Log((int) this.iMember); return; } private void AnoymousFunctionWithLocalArg1() { <>c__DisplayClass6_0 class_; class_ = new <>c__DisplayClass6_0(); class_.bValue = 1; this.actMember = new Action(class_.<AnoymousFunctionWithLocalArg1>b__0); return; } private void AnoymousFunctionWithLocalArg2() { <>c__DisplayClass7_0 class_; class_ = new <>c__DisplayClass7_0(); class_.iValue = 100; this.actMember = new Action(class_.<AnoymousFunctionWithLocalArg2>b__0); return; } private void AnoymousFunctionWithLocalArg3() { <>c__DisplayClass8_0 class_; class_ = new <>c__DisplayClass8_0(); class_.iValue = 1; class_.iValue2 = 2; this.actMember = new Action(class_.<AnoymousFunctionWithLocalArg3>b__0); return; } private void AnoymousFunctionWithMemberArg() { this.actMember = new Action(this.<AnoymousFunctionWithMemberArg>b__5_0); return; } private void AnoymousFunctionWithoutArg() { this.actMember = (<>c.<>9__4_0 != null) ? <>c.<>9__4_0 : (<>c.<>9__4_0 = new Action(this.<AnoymousFunctionWithoutArg>b__4_0)); return; } private void Update() { Profiler.BeginSample("*** AnoymousFunctionWithoutArg ***"); this.AnoymousFunctionWithoutArg(); Profiler.EndSample(); Profiler.BeginSample("*** AnoymousFunctionWithMemberArg ***"); this.AnoymousFunctionWithMemberArg(); Profiler.EndSample(); Profiler.BeginSample("*** AnoymousFunctionWithLocalArg1 ***"); this.AnoymousFunctionWithLocalArg1(); Profiler.EndSample(); Profiler.BeginSample("*** AnoymousFunctionWithLocalArg2 ***"); this.AnoymousFunctionWithLocalArg2(); Profiler.EndSample(); Profiler.BeginSample("*** AnoymousFunctionWithLocalArg3 ***"); this.AnoymousFunctionWithLocalArg3(); Profiler.EndSample(); return; } // Nested Types [Serializable, CompilerGenerated] private sealed class <>c { // Fields public static readonly TestAnonymousFunctionGC.<>c <>9; public static Action <>9__4_0; // Methods static <>c() { <>9 = new TestAnonymousFunctionGC.<>c(); return; } public <>c() { base..ctor(); return; } internal void <AnoymousFunctionWithoutArg>b__4_0() { return; } } [CompilerGenerated] private sealed class <>c__DisplayClass6_0 { // Fields public bool bValue; // Methods public <>c__DisplayClass6_0() { base..ctor(); return; } internal void <AnoymousFunctionWithLocalArg1>b__0() { Debug.Log((bool) this.bValue); return; } } [CompilerGenerated] private sealed class <>c__DisplayClass7_0 { // Fields public int iValue; // Methods public <>c__DisplayClass7_0() { base..ctor(); return; } internal void <AnoymousFunctionWithLocalArg2>b__0() { Debug.Log((int) this.iValue); return; } } [CompilerGenerated] private sealed class <>c__DisplayClass8_0 { // Fields public int iValue; public int iValue2; // Methods public <>c__DisplayClass8_0() { base..ctor(); return; } internal void <AnoymousFunctionWithLocalArg3>b__0() { Debug.Log((int) this.iValue); Debug.Log((int) this.iValue2); return; } } } Collapse Methods
参考:https://blog.uwa4d.com/archives/Anonymous_heapmemory.html
Vector3.Equals函数会有gc:
// Vector3.Equeals有GC 28B { UnityEngine.Profiling.Profiler.BeginSample("*** Vector3.Equals ***"); Vector3 dir1 = Vector3.one, dir2 = Vector3.one; var equals = dir1.Equals(dir2); UnityEngine.Profiling.Profiler.EndSample(); }