【Unity Bug挑战】第6期 静态事件内存泄漏之谜
难度:⭐⭐⭐ 中级
类型:内存管理 / C#交互
环境:全平台支持
Bug 现场 — 停不掉的分数,降不下的内存
小王在做一个游戏的成就系统,用一个静态事件做全局通知:分数变了就广播一次,UI 面板订阅后刷新显示。功能本身五分钟就写完了,测试也没问题。
但玩了几局、来回切了几次场景之后,事情开始不对劲了。
问题场景
代码短得不能再短:静态类持有一个静态事件,触发时广播;面板在 Start 里订阅,收到分数就刷新文本。没有单例、没有全局管理器,看起来干净得很。
AchievementUI.cs
public static class GameEvents { public static event Action<int> OnScoreChanged; public static void TriggerScoreChanged(int score) { if (OnScoreChanged != null) OnScoreChanged(score); } } public class ScoreDisplay : MonoBehaviour { public Text scoreText; void Start() { GameEvents.OnScoreChanged += UpdateScore; } void UpdateScore(int score) { scoreText.text = "分数: " + score; } }诡异现象
跑起来之后,三个现象一个比一个离谱:
现象一:切场景后分数被刷新了好几次——新场景的面板订阅了事件,旧场景那个好像也还在响应
现象二:Profiler 里内存只涨不降,每切一次场景就涨一截,Resources.UnloadUnusedAssets() 也压不下去
现象三:把挂着面板的 GameObject 直接 Destroy 掉,控制台里居然还在打分数更新日志,偶尔还抛 NullReferenceException
预期行为:对象销毁后订阅自动失效,方法不再被调用,内存回落到进入场景前的水平
实际行为:Destroy 之后回调照旧执行,Profiler 的内存曲线一路向上,越玩越卡
代码里没有一处手写引用,对象也确实 Destroy 了,面板也真的从屏幕上消失了——那到底是谁还拉着它不让走?
互动问题
- 为什么 Destroy 了 GameObject 之后,它上面的方法还能被静态事件调用?
- 为什么 Resources.UnloadUnusedAssets() 也释放不了这部分内存?
- 这种情况下,正确的解绑时机应该放在哪里?
#Unity #Bug挑战 #游戏开发 #C #内存管理 #事件系统