UPL10-6——AB包和Resources资源优化

前置知识:U2L10——Resources资源动态加载,UH1——AssetBundle,U4S4——可寻址资源管理系统 Addressables

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

  • AssetBundle 包(或 Addressables)的使用:实现资源热更新、减少包体大小、按需加载
  • Resources 的使用:谨慎使用、避免滥用、了解局限性

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

Resources 使用准则:

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

AssetBundle 包构建流程:

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

AssetBundle 包运行时管理:

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

更新与维护:

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

Resources 系统解析

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

  1. Resources.Load 的成本

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

    作用:快速原型开发和小型项目使用,但不适合中大型项目的主资源管理方案

    建议:仅用于关键启动资源或编辑器工具,绝对避免在运行时频繁调用

  2. Resources.UnloadUnusedAssets

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

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

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

Resources 使用决策流程:

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

AssetBundle 系统解析

AssetBundle 包构建是资源管理的基石

  1. AssetBundle 包压缩方式

    AssetBundle 分三种压缩方式:

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

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

  2. AssetBundle 包依赖关系

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

  3. AB 包粒度规划

    • 细粒度:大量小 AB 包

      • 优点:按需加载,内存占用精准
      • 缺点:I/O 次数多,依赖管理复杂
    • 粗粒度:少量大 AB 包

      • 优点:加载简单,I/O 效率高
      • 缺点:内存浪费,更新不灵活

    建议:

    • 按功能模块划分: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(弱引用,一种不阻止对象被垃圾回收的引用类型)持有资源引用

内存优化与性能监控

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

  1. 内存占用分析

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

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

  2. 加载性能优化

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

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

  3. 泄漏检测与调试

    常见泄漏点:未卸载的AB包、静态变量持有、事件监听未移除

    建议:

    • 自定义 AB 包引用追踪系统
    • 开发阶段开启详细的 AB 包调试日志
    • 实现 AB 包加载的自动化测试用例

AssetBundle 和 Addressables 的选择

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

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

混合使用策略:

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

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