引用引起的内存泄漏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的效果...
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律