111.AB包与Resources资源优化

111.内存优化-资源优化-AB包和Resources


111.1 知识点

AB包与Resources资源管理的核心理念

  • AB 包(或 Addressables)的使用: 实现资源热更新、减少包体大小、按需加载
  • Resources 的使用: 谨慎使用、避免滥用、了解局限性
  • 核心点: 项目中尽量使用 AB 包或者 Addressables 管理加载项目资源

Resources 系统解析

Resources 系统看似简单实则陷阱重重。

Resources.Load 的成本

  • 同步加载: 主线程阻塞,卡顿根源
  • 内存占用: 加载后常驻内存,直到明确卸载
  • 初始化耗时: 首次访问 Resources 文件夹的扫描开销

适合快速原型开发和小型项目使用,但不适合中大型项目的主资源管理方案。建议仅用于关键启动资源或编辑器工具,绝对避免在运行时频繁调用。

Resources.UnloadUnusedAssets

  • 作用: 卸载所有未被引用的资源
  • 代价: 完整 GC 触发,主线程卡顿
  • 原理: 遍历所有资源,检查引用计数为零的资源
  • 建议: 在场景切换等自然断点处调用;避免在性能敏感时段调用;配合 GC.Collect() 使用效果更好

Resources 内存管理陷阱

陷阱 说明
资源泄漏 Resources.Load 后没有对应 Unload
依赖关系 复杂资源依赖导致无法卸载
重复加载 同一资源被多次加载,产生多份实例

建议建立严格的加载/卸载配对机制,使用引用计数管理资源生命周期,并监控 Resources 文件夹总体大小。

Resources 使用决策流程

  • 必须使用的资源 → 启动时预加载 + 引用计数管理
  • 尽量不用 Resources 系统
  • 频繁使用的资源 → 使用 AB 包或 Addressables 替代

AssetBundle 系统解析

AB 包构建是资源管理的基石。

AB 包压缩方式

压缩方式 压缩大小 加载速度 内存消耗 适用场景
LZMA 最小 最慢 最大 首次安装包构建、小体积下载
LZ4 中等 较快 上线项目(推荐)
Uncompressed 最大 最快 大量小文件、频繁加载的情况

90% 的项目推荐用 LZ4。LZ4 是唯一同时兼顾加载速度 + 流式加载能力 + 内存占用合理的格式,是线上热更新 AB 包的行业标准。LZMA 最大的缺点是必须整体解压,占据巨大额外内存——虽然构建出的 AB 是最小的,但运行时 Unity 会将整个 AB 原样读入内存再进行一次整体解压,解压后数据也在内存中,结果导致内存占用大、加载时间还长。

AB 包依赖关系

  • 将公共资源提取到共享 AB 包
  • 避免 AB 包之间双向依赖
  • 控制单个 AB 包的依赖数量

AB 包粒度规划

粒度 优点 缺点
细粒度(大量小 AB 包) 按需加载,内存占用精准 IO 次数多,依赖管理复杂
粗粒度(少量大 AB 包) 加载简单,IO 效率高 内存浪费,更新不灵活

划分建议:

  • 按功能模块划分:UI、角色、场景、配置
  • 按更新频率划分:基础包、热更包、活动包
  • 按内存生命周期划分:常驻、场景、临时

AB 包加载方式

加载卸载策略决定运行时稳定性。

API 说明
AssetBundle.LoadFromFile 从磁盘同步加载
AssetBundle.LoadFromFileAsync 从磁盘异步加载
AssetBundle.LoadFromMemory 从内存数据加载
UnityWebRequestAssetBundle 从网络下载并加载

建议本地 AB 包使用 LoadFromFileAsync,网络 AB 包使用 UnityWebRequestAssetBundle,内存敏感场景避免 LoadFromMemory

AB 包卸载策略

API 行为 风险
AssetBundle.Unload(false) 卸载 AB 包文件,保留实例化对象 可能导致资源缺失(Missing 对象)
AssetBundle.Unload(true) 卸载 AB 包文件及所有创建的对象 可能销毁正在使用的对象

建议使用引用计数机制管理 AB 包生命周期,在安全时机(如场景切换)进行清理,并建立 AB 包卸载的自动化测试。

资源引用管理

  • 问题表现: 资源无法卸载、内存泄漏、Missing 引用
  • 根本原因: 错误的引用持有、循环引用、静态引用
  • 解决方案: 建立资源生命周期监控系统;定期进行资源泄漏检测;使用 WeakReference(弱引用,一种不阻止对象被垃圾回收的引用类型)持有资源引用

内存优化与性能监控

内存管理是 AB 包系统的生命线。

内存占用分析

内存类型 说明
AB 包文件内存 压缩的 AB 包数据
资源对象内存 解压后的资源实例
序列化数据 资源在内存中的表示形式

监控要点:单个 AB 包大小、同时加载的 AB 包数量、资源实例数量、引用关系复杂度。

加载性能优化

  • 预加载: 在场景加载前预加载必要 AB 包
  • 异步加载: 避免主线程阻塞
  • 分帧加载: 将加载压力分摊到多帧

建议建立 AB 包加载优先级系统,监控加载时的帧率变化,设置同时加载的最大 AB 包数量。

泄漏检测与调试

常见泄漏点:未卸载的 AB 包、静态变量持有、事件监听未移除。建议开发阶段开启详细的 AB 包调试日志,实现 AB 包加载的自动化测试用例,并自定义 AB 包引用追踪系统。

AB 包和 Addressables 的选择

  • AB 包优势: 成熟稳定、自定义程度高
  • Addressables 优势: 官方维护、功能丰富、工具链完整

若项目已经有完整的 AB 包加载管理机制,可以不用着急替换为 Addressables,Addressables 的本质也只是官方对 AB 包的封装。

混合使用策略

系统 职责
Resources 启动必备、无法热更的核心资源
AB 包或 Addressables 可热更的游戏内容、大型资源、动态资源

建议明确各系统的职责边界,建立统一的资源加载接口,避免同一资源在多系统中重复存在。

总结

重点:尽量在项目中使用 AB 包或 Addressables 来整体管理资源加载。

Resources 使用准则:

  1. 了解其成本,避免滥用
  2. 建立严格的加载、卸载规范
  3. 监控 Resources 内存占用
  4. 制定向 AB 包或 Addressables 的迁移计划

AB 包构建流程:

  1. 合理规划 AB 包粒度和依赖关系
  2. 选择合适的压缩方式(LZ4)和构建选项
  3. 建立版本管理和热更机制
  4. 实现依赖分析和循环检测

AB 包运行时管理:

  1. 使用引用计数控制生命周期
  2. 异步加载避免主线程阻塞
  3. 安全卸载防止资源缺失
  4. 监控内存和性能指标

更新与维护:

  1. 实现增量更新和版本控制
  2. 建立泄漏检测和调试工具
  3. 定期优化 AB 包组织策略
  4. 准备回滚和容错机制

111.2 知识点代码

Lesson111_内存优化_资源优化_AB包和Resources.cs

public class Lesson111_内存优化_资源优化_AB包和Resources
{
    #region 知识点一 AB包与Resources资源管理的核心理念

    // AB 包(或 Addressables)的使用:
    // 实现资源热更新、减少包体大小、按需加载

    // Resources 的使用:
    // 谨慎使用、避免滥用、了解局限性

    // 核心点:
    // 项目中尽量使用 AB 包或者 Addressables 管理加载项目资源

    #endregion

    #region 知识点二 Resources系统解析

    // Resources 系统看似简单实则陷阱重重

    // 1.Resources.Load 的成本
    //   同步加载:主线程阻塞,卡顿根源
    //   内存占用:加载后常驻内存,直到明确卸载
    //   初始化耗时:首次访问 Resources 文件夹的扫描开销
    //   作用:
    //   快速原型开发和小型项目使用
    //   但不适合中大型项目的主资源管理方案
    //   建议:
    //   仅用于关键启动资源或编辑器工具
    //   绝对避免在运行时频繁调用

    // 2.Resources.UnloadUnusedAssets
    //   作用:卸载所有未被引用的资源
    //   代价:完整 GC 触发,主线程卡顿
    //   原理:遍历所有资源,检查引用计数为零的资源
    //   建议:
    //   在场景切换等自然断点处调用
    //   避免在性能敏感时段调用
    //   配合 GC.Collect() 使用效果更好

    // 3.Resources 内存管理陷阱
    //   资源泄漏:Resources.Load 后没有对应 Unload
    //   依赖关系:复杂资源依赖导致无法卸载
    //   重复加载:同一资源被多次加载,产生多份实例
    //   建议:
    //   建立严格的加载、卸载配对机制
    //   使用引用计数管理资源生命周期
    //   监控 Resources 文件夹总体大小

    // Resources 使用决策流程:
    // 必须使用的资源,启动时预加载 + 引用计数管理
    // 尽量不用 Resources 系统
    // 频繁使用的资源,使用 AB 包或 Addressables 替代

    #endregion

    #region 知识点三 AssetBundle系统解析

    // AB 包构建是资源管理的基石

    // 1.AB 包压缩方式
    //   AB 分三种压缩方式
    //   LZMA:压缩大小最小,加载速度最慢,内存消耗最大,使用场景:首次安装包构建,小体积下载
    //   LZ4:压缩大小中等,加载速度较快,内存消耗小,适合上线项目
    //   Uncompressed(不压缩):压缩大小最大,加载速度最快,内存消耗小,适合大量小文件,频繁加载的情况
    //
    //   90% 的项目推荐用 LZ4
    //   LZ4 是唯一同时兼顾加载速度 + 流式加载能力 + 内存占用合理的格式,是线上热更新 AB 包的行业标准
    //   LZMA 最大的缺点:必须整体解压,占据巨大额外内存
    //   LZMA 构建出的 AB 虽然是最小的
    //   但运行时 Unity 会将整个 AB 原样读入内存,再进行一次整体解压,解压后数据也在内存中
    //   结果就导致内存占用也大,加载时间还长

    // 2.AB 包依赖关系
    //   将公共资源提取到共享 AB 包
    //   避免 AB 包之间双向依赖
    //   控制单个 AB 包的依赖数量

    // 3.AB 包粒度规划
    //   细粒度:大量小 AB 包
    //   优点:按需加载,内存占用精准
    //   缺点:IO 次数多,依赖管理复杂

    //   粗粒度:少量大 AB 包
    //   优点:加载简单,IO 效率高
    //   缺点:内存浪费,更新不灵活
    //   建议:
    //   按功能模块划分:UI、角色、场景、配置
    //   按更新频率划分:基础包、热更包、活动包
    //   按内存生命周期划分:常驻、场景、临时

    // 加载卸载策略决定运行时稳定性
    // 1.AB 包加载方式
    //   AssetBundle.LoadFromFile:从磁盘同步加载
    //   AssetBundle.LoadFromFileAsync:从磁盘异步加载
    //   AssetBundle.LoadFromMemory:从内存数据加载
    //   UnityWebRequestAssetBundle:从网络下载并加载
    //   建议:
    //   本地 AB 包:LoadFromFileAsync
    //   网络 AB 包:UnityWebRequestAssetBundle
    //   内存敏感场景:避免 LoadFromMemory

    // 2.AB 包卸载策略
    //   AssetBundle.Unload(false):卸载 AB 包文件,保留实例化对象
    //   AssetBundle.Unload(true):卸载 AB 包文件及所有创建的对象
    //   风险:
    //   Unload(false):可能导致资源缺失(Missing 对象)
    //   Unload(true):可能销毁正在使用的对象
    //   建议:
    //   使用引用计数机制管理 AB 包生命周期
    //   在安全时机(如场景切换)进行清理
    //   建立 AB 包卸载的自动化测试

    // 3.资源引用管理
    //   问题表现:资源无法卸载、内存泄漏、Missing 引用
    //   根本原因:错误的引用持有、循环引用、静态引用
    //   解决方案:
    //   建立资源生命周期监控系统
    //   定期进行资源泄漏检测
    //   使用 WeakReference(弱引用,一种不阻止对象被垃圾回收的引用类型)持有资源引用

    #endregion

    #region 知识点四 内存优化与性能监控

    // 内存管理是 AB 包系统的生命线
    // 1.内存占用分析
    //   AB 包文件内存:压缩的 AB 包数据
    //   资源对象内存:解压后的资源实例
    //   序列化数据:资源在内存中的表示形式
    //   监控要点:
    //   单个 AB 包大小、同时加载的 AB 包数量
    //   资源实例数量、引用关系复杂度

    // 2.加载性能优化
    //   预加载:在场景加载前预加载必要 AB 包
    //   异步加载:避免主线程阻塞
    //   分帧加载:将加载压力分摊到多帧
    //   建议:
    //   建立 AB 包加载优先级系统
    //   监控加载时的帧率变化
    //   设置同时加载的最大 AB 包数量

    // 3.泄漏检测与调试
    //   常见泄漏点:
    //   未卸载的 AB 包、静态变量持有、事件监听未移除
    //   自定义 AB 包引用追踪系统
    //   建议:
    //   开发阶段开启详细的 AB 包调试日志
    //   实现 AB 包加载的自动化测试用例

    #endregion

    #region 知识点五 AB包和Addressables的选择

    // AB 包优势:成熟稳定、自定义程度高
    // Addressables 优势:官方维护、功能丰富、工具链完整
    // 选择建议:
    // 若项目已经有完整的 AB 包加载管理机制
    // 可以不用着急替换为 Addressables
    // Addressables 的本质也只是官方对 AB 包的封装

    // 混合使用策略
    // Resources:启动必备、无法热更的核心资源
    // AB 包或 Addressables:可热更的游戏内容、大型资源、动态资源
    // 建议:
    // 明确各系统的职责边界
    // 建立统一的资源加载接口
    // 避免同一资源在多系统中重复存在

    #endregion

    #region 总结

    // 重点:尽量在项目中使用 AB 包或 Addressables 来整体管理资源加载

    // Resources 使用准则:
    // 1.了解其成本,避免滥用
    // 2.建立严格的加载、卸载规范
    // 3.监控 Resources 内存占用
    // 4.制定向 AB 包或 Addressables 的迁移计划

    // AB 包构建流程:
    // 1.合理规划 AB 包粒度和依赖关系
    // 2.选择合适的压缩方式(LZ4)和构建选项
    // 3.建立版本管理和热更机制
    // 4.实现依赖分析和循环检测

    // AB 包运行时管理:
    // 1.使用引用计数控制生命周期
    // 2.异步加载避免主线程阻塞
    // 3.安全卸载防止资源缺失
    // 4.监控内存和性能指标

    // 更新与维护:
    // 1.实现增量更新和版本控制
    // 2.建立泄漏检测和调试工具
    // 3.定期优化 AB 包组织策略
    // 4.准备回滚和容错机制

    #endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏