引用引起的内存泄漏2
C#中一个对象的函数, 如果被引用了, 也会导致对象无法被回收, 虽然实际使用中几率很小, 还是记录一下.
using UnityEngine; public class MemoryLeakTest : MonoBehaviour { public class Event { public void Call() { Debug.Log("Event Call"); } ~Event() { Debug.Log("Event Die"); } } System.Action call = null; Event _event = null; private void OnGUI() { if(GUI.Button(new Rect(100, 100, 100, 50), "Event Call")) { _event = new Event(); _event.Call(); call += _event.Call; _event = null; } if(GUI.Button(new Rect(100, 300, 100, 50), "GC")) { Debug.Log("手动GC"); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } } }
创建一个对象, 把对象的Call方法加到Action上, 然后置空引用, 只要对象的方法被引用了, 这样就成了无法GC的对象了.
这个问题的发生属于个人问题, 那么怎样从结构上来避免呢, 如果使用一个弱引用能否避免呢 :
using UnityEngine; public class MonoEvent : MonoBehaviour { public string mark; public void Call() { Debug.Log("MonoEvent Call " + mark); } ~MonoEvent() { Debug.Log("MonoEvent Die " + mark); } }
引用对象MonoEvent
using UnityEngine; public class WeakEventTest : MonoBehaviour { System.WeakReference weakCall = null; private void OnGUI() { if(GUI.Button(new Rect(100, 150, 100, 50), "MonoEvent Call")) { var monoEvent = new GameObject().AddComponent<MonoEvent>(); monoEvent.mark = "111"; monoEvent.Call(); weakCall = new System.WeakReference(new System.Action(monoEvent.Call)); UnityEngine.Object.Destroy(monoEvent.gameObject); } if(GUI.Button(new Rect(100, 210, 100, 50), "手动Call")) { var call = weakCall.Target as System.Action; if(call != null) { call.Invoke(); } else { Debug.Log("引用没了"); } } if(GUI.Button(new Rect(100, 300, 100, 50), "GC")) { Debug.Log("手动GC"); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } } }
运行点击 "MonoEvent Call" 后直接就能析构了, 说明弱引用没有影响到GC. 可是反过来看GC是不是会影响弱引用 :
using UnityEngine; public class WeakEventTest : MonoBehaviour { System.WeakReference weakCall = null; MonoEvent _monoEvent; private void OnGUI() { if(GUI.Button(new Rect(100, 150, 100, 50), "MonoEvent Call")) { var monoEvent = new GameObject().AddComponent<MonoEvent>(); monoEvent.mark = "111"; monoEvent.Call(); weakCall = new System.WeakReference(new System.Action(monoEvent.Call)); _monoEvent = monoEvent; // 添加引用, 不被GC } if(GUI.Button(new Rect(100, 210, 100, 50), "手动Call")) { var call = weakCall.Target as System.Action; if(call != null) { call.Invoke(); } else { Debug.Log("引用没了"); } } if(GUI.Button(new Rect(100, 300, 100, 50), "GC")) { Debug.Log("手动GC"); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } } }
先创建对象, 然后手动GC, 在看看手动Call能否触发, 结果悲剧了弱引用对象没了:
这里引用对象引用的是新创建的System.Action, 它是新对象并且只被弱引用引用, 自然会被GC回收掉了, 所以在系统设计上没有办法用弱引用事件来对对象解耦, 达到不影响GC的效果...