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

 

posted @   tiancaiKG  阅读(817)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示