【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 了,面板也真的从屏幕上消失了——那到底是谁还拉着它不让走?

互动问题

  1. 为什么 Destroy 了 GameObject 之后,它上面的方法还能被静态事件调用?
  2. 为什么 Resources.UnloadUnusedAssets() 也释放不了这部分内存?
  3. 这种情况下,正确的解绑时机应该放在哪里?

#Unity #Bug挑战 #游戏开发 #C #内存管理 #事件系统