You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:25:38