game-assets-memorylisted
Install: claude install-skill Wade-DevCode/awesome-coding-skills-cn
# 资源与内存
## 何时用
- 新增资源加载逻辑(模型、纹理、音效、配置表)之前。
- 发现内存持续增长、场景切换后内存不降、或 Profiler 显示某类资源异常堆积时。
- 接入新的资源管理系统(Addressables、AssetBundle、自研热更)前做方案评审时。
- 出现「卸载了但内存没降」「切场景崩溃」「包体超预算」等问题排查时。
## 核心规则
### 1. 大资源必须异步加载
**规则:** 纹理、音频、场景、模型等大资源一律走异步接口加载,绝不在主线程同步读取;加载期间必须有合适的 loading 表现,加载完成回调后再使用资源。
**为什么:** 最常犯的错是原型阶段用同步加载图方便,上线前"有时间再改"——结果积累了几十处同步加载,进游戏时主线程卡顿 2 秒,ANR 投诉爆仓。另一个坑:异步加载发起后立刻用资源(还没加载完),导致空引用崩溃或用到上一次缓存的错误资源。
**怎么做:**
```csharp
// Unity 示例
// 反例 — 同步加载阻塞主线程
var tex = Resources.Load<Texture2D>("hero/sword"); // ❌ 主线程阻塞
// 正例 — 异步加载,回调中使用
IEnumerator LoadHeroAsync(string key, Action<GameObject> onLoaded) {
var handle = Addressables.LoadAssetAsync<GameObject>(key);
yield return handle; // ✅ 异步等待
if (handle.Status == AsyncOperationStatus.Succeeded) {
onLoaded(handle.Result); // ✅ 加载完再用
} else {
Debug.LogError($"加载失败: {key}");
}
}
```
- 加载过程中显示 loading 进度条或骨骼屏;加载超时(如 10 秒)要有降级或报错逻辑,不能死等。
- 预加载(Preload)在合适时机(进房间动画期间)提前触发,不要等玩家操作时才开始加载。
---
### 2. 引用计数与卸载时机必须明确
**规则:** 每个资源的生命周期(谁持有、何时释放)必须在接入时明确设计;场景切换时主动释放当前场景独占的资源,不依赖 GC 或引擎的"自动"回收。
**为什么:** 最典型的泄漏路径:战斗场景加载了 50 个怪物预制体的纹理,战斗结束切回大厅,没有主动 Release,Addressables 的引用计数没归零,纹理全留在内存里。打完十场战斗内存涨到崩溃。反向问题也有:卸载太激进,在某个地方还持有引用时就强制卸载,运行时出现粉色/错误材质。
**怎么做:**
- 使用 Addressables/AssetBundle ��,每次 `LoadAssetAsync` 对应一次 `Release`,用 RAII 或引用计数封装保证配对。
- 场景切换时有明确的「场景资源卸载」阶段:先通知所有系统释放本场景资源引用,再触发 `UnloadUnusedAssets`。
- 共享资源(公共 UI 图集、常驻音效)单独管理,不随场景卸载;战斗专属资源随战斗结束卸载。
- 建立资源持有关系图(