引用引起的內存泄漏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的效果...

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM