Unity Addressables卸载后内存未恢复至加载前状态问题排查
Addressables加载卸载后内存未完全恢复的排查与解决
问题概述
自定义了Addressables辅助类,结合对象池处理GameObject的加载与卸载逻辑,但卸载后内存仅部分下降,无法回到加载前的状态。通过Memory Profiler分析得到结果:
Event Viewer显示资源已正确释放,但存在未知的InitializationObjectsOperation,且未使用InstantiateAsync方法。
核心加载实现代码
private IEnumerator LoadObjects<TObject>(IList<object> keys, Addressables.MergeMode mergeMode = Addressables.MergeMode.Union) { var locations = Addressables.LoadResourceLocationsAsync(keys, mergeMode, typeof(TObject)); yield return locations; foreach (var location in locations.Result) { if (_allObjectAsyncOperationHandlesByLocationKey.ContainsKey(location.PrimaryKey)) { continue; } var asyncOperationHandle = Addressables.LoadAssetAsync<TObject>(location); asyncOperationHandle.Completed += obj => { if (obj.Status == AsyncOperationStatus.Succeeded) { _allObjectAsyncOperationHandlesByLocationKey.Add(location.PrimaryKey, obj); _allAddressableLocationKeysByObject.Add(obj.Result, location.PrimaryKey); if (obj.Result is GameObject gameObj) OnGameObjectLoaded?.Invoke(gameObj); } else { // TODO: } }; } Addressables.Release(locations); }
问题排查与修复方案
1. 关于InitializationObjectsOperation
这是Addressables内部的初始化异步操作,通常是首次加载资源时自动触发的初始化流程残留,无需手动清除。若内存占用异常,可在游戏启动阶段提前完成Addressables初始化,确保该操作的内存被正确管理:
yield return Addressables.InitializeAsync();
2. 内存未完全恢复的核心原因与修复
- 句柄未正确释放:当前代码仅将
LoadAssetAsync的句柄存入字典,但卸载时未对每个句柄调用Addressables.Release(handle)。对象池销毁GameObject只是销毁实例,Asset本身的句柄未释放会导致内存无法回收。卸载时需遍历_allObjectAsyncOperationHandlesByLocationKey,对每个句柄执行释放操作,同时清空字典避免残留引用。 - 残留引用未清理:检查对象池是否存在未销毁的GameObject实例,或是否有静态引用(如未取消的事件订阅、单例持有Asset引用),这些会阻止GC回收Asset。
- GC触发时机:内存未恢复可能是GC尚未触发,可在卸载完成后手动触发GC并等待回收完成:
GC.Collect(); GC.WaitForPendingFinalizers();
3. 加载逻辑优化
建议将异步回调改为协程等待,确保句柄被可靠跟踪,同时避免回调中可能的线程安全问题:
private IEnumerator LoadObjects<TObject>(IList<object> keys, Addressables.MergeMode mergeMode = Addressables.MergeMode.Union) { var locations = Addressables.LoadResourceLocationsAsync(keys, mergeMode, typeof(TObject)); yield return locations; foreach (var location in locations.Result) { if (_allObjectAsyncOperationHandlesByLocationKey.ContainsKey(location.PrimaryKey)) { continue; } var asyncOperationHandle = Addressables.LoadAssetAsync<TObject>(location); yield return asyncOperationHandle; if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded) { _allObjectAsyncOperationHandlesByLocationKey.Add(location.PrimaryKey, asyncOperationHandle); _allAddressableLocationKeysByObject.Add(asyncOperationHandle.Result, location.PrimaryKey); if (asyncOperationHandle.Result is GameObject gameObj) OnGameObjectLoaded?.Invoke(gameObj); } else { // 错误处理:释放失败的句柄 Addressables.Release(asyncOperationHandle); } } Addressables.Release(locations); }
内容的提问来源于stack exchange,提问作者Charles Nough
相关产品推荐
相关产品推荐

