05-07-YooAsset源码-Unity引用计数与资源生命周期

📅 2026/7/17 5:25:54
05-07-YooAsset源码-Unity引用计数与资源生命周期
源码-引用计数机制篇章05-源码深度-运行时核心模块阅读时间约 25 分钟前置知识了解 Provider 体系一、引言本章将深入解析 YooAsset 引用计数机制的源码实现。引用计数是 YooAsset 资源管理的核心机制用于管理资源的引用关系和生命周期。深入理解引用计数机制是掌握 YooAsset 资源管理的关键。引用计数机制的设计目标是确保资源在不再使用时及时释放避免内存泄漏。本章将从源码层面深入解析引用计数机制的实现。二、引用计数概述2.1 引用计数的概念引用计数是一种资源管理技术引用计数的定义引用计数记录了资源被引用的次数。每次资源被引用时引用计数加 1每次引用被释放时引用计数减 1。引用计数的作用跟踪资源的引用关系决定资源的生命周期避免资源过早释放避免内存泄漏引用计数的特点实时性引用计数实时更新准确性只要遵守规则引用计数是准确的自动管理资源生命周期自动管理2.2 YooAsset 中的引用计数YooAsset 中的引用计数应用于以下对象Provider资源提供者BundleLoaderBundle 加载器AssetBundleAssetBundle 实例Provider 引用计数记录资源被引用的次数。引用为 0 时Provider 销毁。BundleLoader 引用计数记录 Bundle 被引用的次数。引用为 0 时Bundle 销毁。AssetBundle 引用计数记录 AssetBundle 被 Provider 引用的次数。2.3 引用计数的实现public abstract class RefCountOperation { private int _refCount 0; public int RefCount _refCount; public void IncreaseRefCount() { Interlocked.Increment(ref _refCount); } public void DecreaseRefCount() { Interlocked.Decrement(ref _refCount); } }Interlocked.Increment 详解使用 Interlocked.Increment 进行原子操作确保多线程环境下的引用计数正确性。Interlocked.Decrement 详解使用 Interlocked.Decrement 进行原子操作。三、Provider 引用计数3.1 Provider 引用计数的作用Provider 引用计数记录了资源被引用的次数Provider 引用计数的应用场景同一资源被多个地方引用异步加载时多次调用 LoadAssetAsync长生命周期的对象引用短生命周期的资源3.2 Provider 引用计数的实现public abstract class ProviderBase { private int _refCount 0; public int RefCount _refCount; public void IncreaseRefCount() { Interlocked.Increment(ref _refCount); } public void DecreaseRefCount() { Interlocked.Decrement(ref _refCount); if (_refCount 0) { // 引用为 0销毁 Provider Destroy(); } } }引用计数增加详解LoadAssetAsync 调用时引用计数加 1。引用计数减少详解UnloadAsset 调用时引用计数减 1。销毁触发详解引用为 0 时Provider 自动销毁。3.3 Provider 引用计数的应用// 场景 1基本用法 var handle package.LoadAssetAsyncGameObject(prefab); Instantiate(handle.AssetObject); handle.Release(); // 引用 -1销毁资源 // 场景 2多次加载同一资源 var handle1 package.LoadAssetAsyncGameObject(prefab); var handle2 package.LoadAssetAsyncGameObject(prefab); // handle1 和 handle2 引用同一 Provider handle1.Release(); // 引用 -1 handle2.Release(); // 引用 -1销毁资源 // 场景 3异步加载中的引用 var handle package.LoadAssetAsyncGameObject(prefab); // 异步加载中handle 还在等待 await handle.Task; Instantiate(handle.AssetObject); handle.Release();基本用法详解LoadAssetAsync 加载资源Release 释放资源。多次加载详解多次加载同一资源会复用同一 Provider引用计数累加。异步加载详解异步加载中引用计数保持确保资源不会被过早释放。四、BundleLoader 引用计数4.1 BundleLoader 引用计数的作用BundleLoader 引用计数记录了 Bundle 被引用的次数BundleLoader 引用计数的应用场景多个资源引用同一 BundleProvider 加载时引用 BundleProvider 卸载时减少引用4.2 BundleLoader 引用计数的实现public class BundleLoader { private int _refCount 0; public int RefCount _refCount; public void Retain() { Interlocked.Increment(ref _refCount); } public void Release() { Interlocked.Decrement(ref _refCount); if (_refCount 0) { UnloadBundle(); } } private void UnloadBundle() { if (_assetBundle ! null) { _assetBundle.Unload(true); _assetBundle null; } } }引用计数增加详解Provider 加载资源时会调用 BundleLoader.Retain 增加引用。引用计数减少详解Provider 销毁时会调用 BundleLoader.Release 减少引用。Bundle 卸载详解引用为 0 时BundleLoader 自动卸载 AssetBundle。4.3 BundleLoader 引用计数的应用public class ResourceManager { public BundleLoader LoadBundle(BundleInfo bundleInfo) { string key bundleInfo.FileName; if (_bundleLoaders.TryGetValue(key, out var loader)) { loader.Retain(); return loader; } loader new BundleLoader(bundleInfo); loader.LoadAsync(); _bundleLoaders[key] loader; loader.Retain(); return loader; } public void UnloadBundle(BundleLoader loader) { loader.Release(); if (loader.RefCount 0) { _bundleLoaders.Remove(loader.BundleInfo.FileName); } } }BundleLoader 获取详解LoadBundle 方法获取 BundleLoader如果已存在则复用否则创建。引用计数管理详解LoadBundle 增加引用UnloadBundle 减少引用。自动卸载详解引用为 0 时BundleLoader 自动从缓存中移除。五、引用计数与依赖管理5.1 依赖资源管理资源之间的依赖关系也需要引用计数管理public class AssetProvider : ProviderBase { private ListBundleLoader _dependLoaders; public override void Provide() { // 加载资源 var assetInfo Package.GetAssetInfo(Location); // 加载依赖 Bundle _dependLoaders new ListBundleLoader(); foreach (var depBundleID in assetInfo.DependIDs) { var depBundle Package.GetBundleInfo(depBundleID); var loader Package.ResourceManager.LoadBundle(depBundle); _dependLoaders.Add(loader); } } public override void UnProvide() { // 释放依赖 Bundle foreach (var loader in _dependLoaders) { Package.ResourceManager.UnloadBundle(loader); } _dependLoaders.Clear(); } }依赖加载详解加载资源时同时加载所有依赖的 Bundle。依赖释放详解释放资源时同时释放所有依赖的 Bundle。引用计数管理详解通过 LoadBundle 和 UnloadBundle 管理依赖的引用计数。5.2 共享 Bundle 管理共享 Bundle 被多个资源引用需要特殊处理public class ResourceManager { public BundleLoader GetOrLoadBundle(BundleInfo bundleInfo) { string key bundleInfo.FileName; if (_bundleLoaders.TryGetValue(key, out var loader)) { // 已存在复用并增加引用 loader.Retain(); return loader; } // 不存在创建 loader new BundleLoader(bundleInfo); loader.LoadAsync(); _bundleLoaders[key] loader; loader.Retain(); return loader; } }共享 Bundle 处理详解共享 Bundle 通过 BundleLoader 缓存实现共享避免重复加载。引用计数作用详解引用计数确保共享 Bundle 在所有引用都释放后才卸载。六、引用计数注意事项6.1 配对原则引用计数必须遵守配对原则Load-Release 配对每次 Load 必须有对应的 Release。配对检查public class ProviderBase { public void VerifyRefCount() { if (RefCount 0) { Debug.LogError($Provider {Location} ref count is negative!); } if (RefCount 0 Status ! EProviderStatus.Destroyed) { Debug.LogWarning($Provider {Location} has zero ref count but not destroyed!); } } }调试方法详解通过 VerifyRefCount 方法检查引用计数是否正确。6.2 异步引用计数异步加载中需要正确管理引用计数// 错误异步加载中提前释放 var handle package.LoadAssetAsyncGameObject(prefab); handle.Release(); // 错误异步加载还未完成 Instantiate(handle.AssetObject); // 资源可能已被销毁 // 正确等待异步加载完成后释放 var handle package.LoadAssetAsyncGameObject(prefab); await handle.Task; // 等待加载完成 Instantiate(handle.AssetObject); handle.Release(); // 正确释放异步加载注意事项详解异步加载中引用计数保持确保资源不会过早释放。6.3 循环引用循环引用会导致引用计数永远不为 0避免循环引用资源之间不应该有循环引用使用弱引用避免循环引用定期检查引用计数循环引用检测public class RefCountCycleDetector { public ListProviderBase DetectCycles() { // 使用图算法检测循环引用 // ... } }七、总结本章深入解析了 YooAsset 引用计数机制的源码实现包括引用计数概述引用计数的概念、YooAsset 中的应用、实现Provider 引用计数Provider 引用计数的作用、实现、应用BundleLoader 引用计数BundleLoader 引用计数的作用、实现、应用引用计数与依赖管理依赖资源管理、共享 Bundle 管理引用计数注意事项配对原则、异步引用、循环引用引用计数机制是 YooAsset 资源管理的核心通过深入理解引用计数机制开发者可以避免内存泄漏正确管理资源生命周期。上一篇异步体系OperationSystem下一篇资源缓存机制