mirror of
https://github.com/tuyoogame/YooAsset.git
synced 2026-05-19 06:40:21 +00:00
31 KiB
31 KiB
DiagnosticSystem 诊断系统
模块概述
DiagnosticSystem 是 YooAsset 的远程调试诊断系统,提供运行时资源管理状态的实时可视化和性能分析能力。该系统通过编辑器窗口与运行时游戏进行双向通信,实时采集和展示资源加载、Bundle 管理、异步操作等调试信息。
核心特性
- 实时远程调试:在 Unity 编辑器中查看游戏运行时的资源管理状态
- 完整状态快照:采集所有资源、Bundle、异步操作的实时信息
- 历史数据回溯:缓存最近 500 帧数据,支持时间回溯分析
- 双模式采样:支持单次采样和自动连续采样
- 低性能开销:按需采样,无需连续监控
模块统计
| 组件 | 职责 |
|---|---|
| 核心通信 | RemoteDebuggerInRuntime + 双连接层 |
| 数据结构 | 5 个调试信息结构体 |
| 命令系统 | RemoteCommand 命令定义 |
| 总计 | 10 个核心文件,完整的远程诊断框架 |
设计目标
| 目标 | 说明 |
|---|---|
| 实时可视化 | 在编辑器中实时查看运行时资源状态 |
| 性能监控 | 收集加载耗时、引用计数、下载进度等性能指标 |
| 内存诊断 | 追踪资源出生场景、引用计数,识别潜在内存泄漏 |
| 操作追踪 | 显示异步操作树,包括嵌套和依赖关系 |
| 低开销设计 | 按需采样而非连续监控,DEBUG 模式自动启用 |
文件结构
DiagnosticSystem/
├── RemoteDebuggerDefine.cs # 全局定义和常量
├── RemoteCommand.cs # 命令定义和序列化
├── DebugReport.cs # 调试报告(顶层容器)
├── DebugPackageData.cs # 包级调试数据
├── DebugProviderInfo.cs # 资源加载器调试信息
├── DebugBundleInfo.cs # 资源包调试信息
├── DebugOperationInfo.cs # 异步操作调试信息
├── RemoteDebuggerInRuntime.cs # 运行时调试器主类
├── RemotePlayerConnection.cs # 编辑器模拟连接层
└── RemoteEditorConnection.cs # 运行时模拟连接层
核心类说明
RemoteDebuggerDefine
全局定义类,包含调试器版本和通信协议的 GUID 标识符。
internal class RemoteDebuggerDefine
{
// 调试器版本(用于版本校验)
public const string DebuggerVersion = "2.3.3";
// 消息标识符(GUID)
public static readonly Guid kMsgPlayerSendToEditor =
new Guid("e34a5702dd353724aa315fb8011f08c3"); // 运行时→编辑器
public static readonly Guid kMsgEditorSendToPlayer =
new Guid("4d1926c9df5b052469a1c63448b7609a"); // 编辑器→运行时
}
RemoteCommand
命令定义类,用于编辑器向运行时发送采样指令。
internal enum ERemoteCommand
{
SampleOnce = 0, // 单次采样
SampleAuto = 1, // 自动采样(连续)
}
[Serializable]
internal class RemoteCommand
{
public int CommandType; // ERemoteCommand 枚举值
public string CommandParam; // 命令参数
// 序列化/反序列化(JSON 格式)
public static byte[] Serialize(RemoteCommand command)
{
return Encoding.UTF8.GetBytes(JsonUtility.ToJson(command));
}
public static RemoteCommand Deserialize(byte[] data)
{
return JsonUtility.FromJson<RemoteCommand>(Encoding.UTF8.GetString(data));
}
}
命令示例:
// 单次采样
{
"CommandType": 0,
"CommandParam": ""
}
// 开启自动采样
{
"CommandType": 1,
"CommandParam": "open"
}
// 关闭自动采样
{
"CommandType": 1,
"CommandParam": "close"
}
DebugReport
调试报告容器,包含完整的系统状态快照。
[Serializable]
internal class DebugReport
{
// 调试器版本(用于版本校验)
public string DebuggerVersion = RemoteDebuggerDefine.DebuggerVersion;
// 游戏帧数
public int FrameCount;
// 包级调试数据列表(一个游戏可能有多个资源包)
public List<DebugPackageData> PackageDatas = new List<DebugPackageData>(10);
// 序列化/反序列化
public static byte[] Serialize(DebugReport debugReport);
public static DebugReport Deserialize(byte[] data);
}
DebugPackageData
包级调试数据,包含单个资源包的所有诊断信息。
[Serializable]
internal class DebugPackageData
{
// 资源包名称
public string PackageName;
// 资源加载器列表
public List<DebugProviderInfo> ProviderInfos = new List<DebugProviderInfo>(1000);
// 资源包列表
public List<DebugBundleInfo> BundleInfos = new List<DebugBundleInfo>(1000);
// 异步操作列表
public List<DebugOperationInfo> OperationInfos = new List<DebugOperationInfo>(1000);
// 运行时查询字典(非序列化,按需构建)
[NonSerialized]
public Dictionary<string, DebugBundleInfo> BundleInfoDic;
// 延迟解析字典
public DebugBundleInfo GetBundleInfo(string bundleName);
}
DebugProviderInfo
资源加载器(Provider)的调试信息。
[Serializable]
internal struct DebugProviderInfo : IComparer<DebugProviderInfo>, IComparable<DebugProviderInfo>
{
public string PackageName; // 所属包名
public string AssetPath; // 资源路径(如 "Assets/Prefabs/Player.prefab")
public string SpawnScene; // 资源加载时的活跃场景名
public string BeginTime; // 加载开始时间(格式:HH:mm:ss.fff)
public long LoadingTime; // 加载耗时(单位:毫秒)
public int RefCount; // 引用计数
public string Status; // 加载状态(None/Processing/Succeed/Failed)
public List<string> DependBundles; // 依赖的资源包名列表
// 按 AssetPath 字母排序
public int CompareTo(DebugProviderInfo other);
}
关键诊断价值:
SpawnScene:追踪资源在哪个场景被加载,帮助识别资源泄漏LoadingTime:性能分析,识别加载慢的资源RefCount:引用计数监控,RefCount > 0 表示资源仍在使用DependBundles:依赖分析,理解资源加载的完整依赖链
DebugBundleInfo
资源包(Bundle)的调试信息。
[Serializable]
internal struct DebugBundleInfo : IComparer<DebugBundleInfo>, IComparable<DebugBundleInfo>
{
public string BundleName; // 资源包名称
public int RefCount; // 引用计数(当前有多少个 Provider 在使用)
public string Status; // 加载状态
public List<string> ReferenceBundles; // 反向依赖(谁引用了我)
// 按 BundleName 字母排序
public int CompareTo(DebugBundleInfo other);
}
关键诊断价值:
RefCount:Bundle 引用计数,为 0 时可以被卸载ReferenceBundles:反向依赖分析,了解 Bundle 被哪些其他 Bundle 依赖
DebugOperationInfo
异步操作的调试信息,支持递归树结构。
[Serializable]
internal struct DebugOperationInfo : IComparer<DebugOperationInfo>, IComparable<DebugOperationInfo>
{
public string OperationName; // 操作类名(如 "LoadAssetOperation")
public string OperationDesc; // 操作说明(自定义描述)
public uint Priority; // 优先级(用于操作排序)
public float Progress; // 进度(0.0 - 1.0)
public string BeginTime; // 操作开始时间
public long ProcessTime; // 处理耗时(单位:毫秒)
public string Status; // 操作状态(None/Processing/Succeed/Failed)
public List<DebugOperationInfo> Childs; // 子操作列表(支持嵌套树结构)
public int CompareTo(DebugOperationInfo other);
}
递归树结构示例:
InitializationOperation
├─ LoadManifestOperation
│ ├─ LoadBundleFileOperation (manifest.bundle)
│ └─ DeserializeManifestOperation
└─ InitFileSystemOperation
关键诊断价值:
OperationName:操作类型识别ProcessTime:性能瓶颈分析Childs:操作依赖关系可视化
通信系统
RemoteDebuggerInRuntime
运行时调试器主类,负责接收命令、采样数据、发送报告。
internal class RemoteDebuggerInRuntime : MonoBehaviour
{
// 采样控制标志
private static bool _sampleOnce = false; // 单次采样
private static bool _autoSample = false; // 连续采样
// 运行时初始化
[RuntimeInitializeOnLoadMethod]
private static void RuntimeInitializeOnLoad()
{
_sampleOnce = false;
_autoSample = false;
}
private void Awake()
{
RemotePlayerConnection.Instance.Initialize();
}
private void OnEnable()
{
// 注册命令接收回调
RemotePlayerConnection.Instance.Register(
RemoteDebuggerDefine.kMsgEditorSendToPlayer,
OnHandleEditorMessage);
}
private void LateUpdate()
{
// 采样逻辑(在一帧的最后执行)
if (_autoSample || _sampleOnce)
{
_sampleOnce = false;
var debugReport = YooAssets.GetDebugReport();
var data = DebugReport.Serialize(debugReport);
RemotePlayerConnection.Instance.Send(
RemoteDebuggerDefine.kMsgPlayerSendToEditor,
data);
}
}
private static void OnHandleEditorMessage(MessageEventArgs args)
{
var command = RemoteCommand.Deserialize(args.data);
if (command.CommandType == (int)ERemoteCommand.SampleOnce)
{
_sampleOnce = true;
}
else if (command.CommandType == (int)ERemoteCommand.SampleAuto)
{
_autoSample = (command.CommandParam == "open");
}
}
}
关键设计点:
- LateUpdate 时机:确保该帧所有资源加载完成后再采样
- 状态重置:
[RuntimeInitializeOnLoadMethod]确保编辑器重新编译时重置状态 - DEBUG 模式自动启用:通过
#if DEBUG条件编译自动添加组件
双连接层架构
YooAsset 支持两种通信模式:
| 模式 | 使用场景 | 实现方式 |
|---|---|---|
| 编辑器模拟模式 | 开发调试 | RemotePlayerConnection + RemoteEditorConnection(虚拟连接) |
| 发布版本 | 运营期监控 | Unity 的 PlayerConnection API(真实网络) |
RemotePlayerConnection(编辑器模拟模式)
internal class RemotePlayerConnection
{
private static RemotePlayerConnection _instance;
private readonly Dictionary<Guid, UnityAction<MessageEventArgs>> _messageCallbacks;
public static RemotePlayerConnection Instance
{
get
{
if (_instance == null)
_instance = new RemotePlayerConnection();
return _instance;
}
}
public void Register(Guid messageID, UnityAction<MessageEventArgs> callback)
{
_messageCallbacks.Add(messageID, callback);
}
public void Send(Guid messageID, byte[] data)
{
// 在编辑器模拟模式下,发送给虚拟编辑器连接
RemoteEditorConnection.Instance.HandlePlayerMessage(messageID, data);
}
internal void HandleEditorMessage(Guid messageID, byte[] data)
{
if (_messageCallbacks.TryGetValue(messageID, out var callback))
{
callback.Invoke(new MessageEventArgs { playerId = 0, data = data });
}
}
}
RemoteEditorConnection(运行时模拟模式)
internal class RemoteEditorConnection
{
private static RemoteEditorConnection _instance;
private readonly Dictionary<Guid, UnityAction<MessageEventArgs>> _messageCallbacks;
public static RemoteEditorConnection Instance
{
get
{
if (_instance == null)
_instance = new RemoteEditorConnection();
return _instance;
}
}
public void Register(Guid messageID, UnityAction<MessageEventArgs> callback)
{
_messageCallbacks.Add(messageID, callback);
}
public void Send(Guid messageID, byte[] data)
{
// 发送给虚拟运行时连接
RemotePlayerConnection.Instance.HandleEditorMessage(messageID, data);
}
internal void HandlePlayerMessage(Guid messageID, byte[] data)
{
if (_messageCallbacks.TryGetValue(messageID, out var callback))
{
callback.Invoke(new MessageEventArgs { playerId = 0, data = data });
}
}
}
通信协议
协议规范
协议版本: 2.3.3
编码格式:
C# 对象 → JsonUtility.ToJson() → JSON 字符串 → Encoding.UTF8.GetBytes() → byte[]
消息类型:
| 方向 | GUID | 数据类型 | 说明 |
|---|---|---|---|
| 编辑器→运行时 | 4d1926c9df5b052469a1c63448b7609a |
RemoteCommand |
采样命令 |
| 运行时→编辑器 | e34a5702dd353724aa315fb8011f08c3 |
DebugReport |
调试报告 |
双向通信流程
[编辑器 UI]
│
├─ 用户点击 "Sample" 按钮
│ └─ 发送 RemoteCommand (SampleOnce)
│
└─ 用户开启 "Record" 开关
└─ 发送 RemoteCommand (SampleAuto, "open")
↓ RemoteEditorConnection.Send()
↓
RemotePlayerConnection.HandleEditorMessage()
↓ 触发回调
[运行时]
RemoteDebuggerInRuntime.OnHandleEditorMessage()
↓
设置采样标志 (_sampleOnce 或 _autoSample)
↓
LateUpdate 中采样
↓
YooAssets.GetDebugReport()
├─ 收集所有 ResourcePackage 数据
├─ DebugProviderInfo[] (从 ProviderDic)
├─ DebugBundleInfo[] (从 LoaderDic)
└─ DebugOperationInfo[] (从 _operations)
↓
DebugReport.Serialize()
↓
RemotePlayerConnection.Send()
↓
RemoteEditorConnection.HandlePlayerMessage()
↓
[编辑器]
AssetBundleDebuggerWindow.OnHandlePlayerMessage()
↓
版本校验 (DebuggerVersion)
↓
RemotePlayerSession.AddDebugReport()
↓
缓存到历史记录 (最多 500 帧)
↓
UI 刷新显示
版本校验机制
// 编辑器端版本校验
private void OnHandlePlayerMessage(MessageEventArgs args)
{
var debugReport = DebugReport.Deserialize(args.data);
// 版本校验
if (debugReport.DebuggerVersion != RemoteDebuggerDefine.DebuggerVersion)
{
Debug.LogWarning(
$"Debugger versions are inconsistent : " +
$"{debugReport.DebuggerVersion} != {RemoteDebuggerDefine.DebuggerVersion}");
return; // 丢弃不兼容的数据
}
// 处理数据...
}
设计意图: 防止编辑器和运行时的调试器版本不一致导致的数据格式错误。
数据收集流程
完整采样流程
RemoteDebuggerInRuntime.LateUpdate()
↓
检查 _sampleOnce 或 _autoSample 标志
↓ YES
调用 YooAssets.GetDebugReport()
│
├─ 初始化 DebugReport
├─ 设置 FrameCount = Time.frameCount
├─ 遍历每个 ResourcePackage:
│ │
│ └─ package.GetDebugPackageData()
│ │
│ ├─ 创建 DebugPackageData
│ ├─ 设置 PackageName
│ │
│ ├─ 收集 ProviderInfos:
│ │ ResourceManager.GetDebugProviderInfos()
│ │ 遍历 ProviderDic:
│ │ └─ 每个 ProviderOperation 提供:
│ │ - MainAssetInfo.AssetPath
│ │ - SpawnScene(场景名)
│ │ - BeginTime(开始时间)
│ │ - ProcessTime(耗时)
│ │ - RefCount(引用计数)
│ │ - Status(加载状态)
│ │ - GetDebugDependBundles()(依赖列表)
│ │
│ ├─ 收集 BundleInfos:
│ │ ResourceManager.GetDebugBundleInfos()
│ │ 遍历 LoaderDic:
│ │ └─ 每个 LoadBundleFileOperation 提供:
│ │ - BundleName
│ │ - RefCount
│ │ - Status
│ │ - FilterReferenceBundles()(反向依赖)
│ │
│ └─ 收集 OperationInfos:
│ OperationSystem.GetDebugOperationInfos(PackageName)
│ 遍历 _operations(按 PackageName 过滤):
│ └─ 递归构建操作树:
│ - GetType().Name(操作类名)
│ - GetOperationDesc()(自定义描述)
│ - Priority(优先级)
│ - Progress(进度)
│ - BeginTime(开始时间)
│ - ProcessTime(耗时)
│ - Status(状态)
│ - Childs(子操作列表)
│
└─ 返回 DebugReport
性能指标收集
资源加载耗时
// AsyncOperationBase 中的自动计时
private Stopwatch _watch = null;
internal void InternalStart()
{
if (_watch == null)
{
BeginTime = SpawnTimeToString(UnityEngine.Time.realtimeSinceStartup);
_watch = Stopwatch.StartNew();
}
}
internal void InternalUpdate()
{
ProcessTime = _watch.ElapsedMilliseconds;
// ... 持续计时
}
场景信息追踪
// ProviderOperation.cs
[Conditional("DEBUG")] // 仅在 DEBUG 模式下启用
public void InitProviderDebugInfo()
{
SpawnScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
}
引用计数监控
// ProviderOperation 和 LoadBundleFileOperation 都维护 RefCount
public int RefCount { get; } // 当前被引用的次数
与其他模块的交互
YooAssets (全局入口)
│
├─ 初始化阶段:
│ #if DEBUG
│ _driver.AddComponent<RemoteDebuggerInRuntime>();
│ #endif
│
└─ 数据收集入口:
GetDebugReport()
├─ 遍历所有 ResourcePackage
└─ 构建 DebugReport
ResourcePackage (资源包)
│
└─ GetDebugPackageData()
├─ 调用 ResourceManager.GetDebugProviderInfos()
├─ 调用 ResourceManager.GetDebugBundleInfos()
└─ 调用 OperationSystem.GetDebugOperationInfos()
ResourceManager (资源管理器)
│
├─ GetDebugProviderInfos()
│ └─ 遍历 ProviderDic (Dictionary<string, ProviderOperation>)
│
└─ GetDebugBundleInfos()
└─ 遍历 LoaderDic (Dictionary<string, LoadBundleFileOperation>)
OperationSystem (操作系统)
│
└─ GetDebugOperationInfos(packageName)
└─ 遍历 _operations (List<AsyncOperationBase>)
└─ 递归收集子操作 (Childs)
AsyncOperationBase (异步操作基类)
│
├─ BeginTime:操作开始时间
├─ ProcessTime:累计处理耗时
├─ Status:操作状态
├─ Progress:进度
└─ GetOperationDesc():自定义描述
ProviderOperation (资源提供者)
│
├─ SpawnScene:加载时的活跃场景
└─ GetDebugDependBundles():依赖包列表
LoadBundleFileOperation (Bundle 加载器)
│
├─ RefCount:引用计数
└─ LoadBundleInfo:Bundle 信息
使用场景
场景 1:运行时资源泄漏诊断
问题: 游戏切换场景后内存持续增长,怀疑有资源未释放。
诊断步骤:
- 打开 AssetBundle Debugger 窗口
- 开启 Record 模式(自动采样)
- 切换场景前后观察 ProviderInfos 列表
- 检查
RefCount > 0且SpawnScene为旧场景的资源 - 定位未释放的资源和对应的代码位置
关键字段:
SpawnScene:资源在哪个场景被加载RefCount:引用计数,应该为 0AssetPath:资源路径,定位具体资源
场景 2:资源加载性能分析
问题: 首次加载场景卡顿严重。
诊断步骤:
- 单次采样(Sample Once)
- 切换到 Asset View
- 按
LoadingTime降序排序 - 识别加载耗时最长的资源
- 分析
DependBundles了解依赖链
关键字段:
LoadingTime:加载耗时(毫秒)DependBundles:依赖的 Bundle 列表Status:加载状态
场景 3:Bundle 引用分析
问题: 某个 Bundle 无法被卸载。
诊断步骤:
- 切换到 Bundle View
- 搜索目标 Bundle
- 检查
RefCount和ReferenceBundles - 追踪哪些资源正在使用该 Bundle
- 定位未释放的资源引用
关键字段:
RefCount:Bundle 引用计数ReferenceBundles:反向依赖列表Status:Bundle 加载状态
场景 4:异步操作监控
问题: 复杂的初始化流程卡住,不知道在哪个步骤。
诊断步骤:
- 切换到 Operation View
- 查看操作树结构
- 检查
Status为Processing的操作 - 分析
Progress了解进度 - 通过
Childs了解操作依赖关系
关键字段:
OperationName:操作类型OperationDesc:操作描述Progress:进度(0.0 - 1.0)Childs:子操作列表
数据导出
JSON 导出功能
编辑器窗口支持导出当前帧的完整调试数据为 JSON 文件。
导出示例:
{
"DebuggerVersion": "2.3.3",
"FrameCount": 1234,
"PackageDatas": [
{
"PackageName": "DefaultPackage",
"ProviderInfos": [
{
"PackageName": "DefaultPackage",
"AssetPath": "Assets/Prefabs/Player.prefab",
"SpawnScene": "GameScene",
"BeginTime": "12:34:56.789",
"LoadingTime": 45,
"RefCount": 1,
"Status": "Succeed",
"DependBundles": [
"assets_prefabs.bundle"
]
}
],
"BundleInfos": [
{
"BundleName": "assets_prefabs.bundle",
"RefCount": 1,
"Status": "Succeed",
"ReferenceBundles": []
}
],
"OperationInfos": [
{
"OperationName": "LoadAssetOperation",
"OperationDesc": "Load assets_prefabs.bundle",
"Priority": 0,
"Progress": 1.0,
"BeginTime": "12:34:56.745",
"ProcessTime": 44,
"Status": "Succeed",
"Childs": []
}
]
}
]
}
用途:
- 离线分析和归档
- 性能数据对比
- 问题复现和追踪
系统架构图
┌─────────────────────────────────────────────────────────────────────┐
│ Unity Editor Window │
│ AssetBundleDebuggerWindow │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ UI Controls: │ │
│ │ - Sample Button (SampleOnce) │ │
│ │ - Record Toggle (SampleAuto) │ │
│ │ - View Mode Menu (Asset/Bundle/Operation View) │ │
│ │ - Frame Slider (历史帧导航) │ │
│ │ - Search Field (关键词搜索) │ │
│ │ - Export Button (JSON 导出) │ │
│ └──────────────────────────────────────────────────────────────┘ │
└────────────┬──────────────────────────────────────────────────────────┘
│
┌─────▼───────────────────────────────────┐
│ RemoteEditorConnection (虚拟连接) │
│ - Register callbacks │
│ - Send/Receive commands & reports │
└─────┬──────────────────────────────┬────┘
│ │
┌────────▼─────────────┐ ┌──────────▼───────────────┐
│ RemoteCommand │ │ DebugReport │
│ (Serialize) │ │ (Deserialize) │
│ ↓ JSON │ │ ← JSON │
│ ↓ UTF-8 bytes │ │ ← UTF-8 bytes │
└────────┬─────────────┘ └──────────┬───────────────┘
│ │
│ ═══════════════════════ │
│ Internet / Emulation │
│ ═══════════════════════ │
│ │
┌────────▼─────────────┐ ┌──────────▼───────────────┐
│ PlayerConnection │ │ RemotePlayerConnection │
│ (真实连接) │ │ (虚拟连接) │
│ 或模拟连接 │ │ │
└────────┬─────────────┘ └──────────┬───────────────┘
│ │
└──────────────┬───────────────┘
│
┌──────▼──────────────────────────────┐
│ RemoteDebuggerInRuntime │
│ (MonoBehaviour) │
│ ┌──────────────────────────────┐ │
│ │ _sampleOnce (bool) │ │
│ │ _autoSample (bool) │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Awake() - 初始化连接 │ │
│ │ OnEnable() - 注册回调 │ │
│ │ LateUpdate() - 采样触发 │ │
│ │ OnHandleEditorMessage() - 收命令│ │
│ └──────────────────────────────┘ │
└──────────┬──────────────────────────┘
│
┌──────▼────────────────────┐
│ YooAssets.GetDebugReport() │
│ (全系统数据收集入口) │
└──────┬────────────────────┘
│
┌───────────┼───────────┐
│ │ │
┌──────────▼──┐ ┌─────▼────┐ ┌──▼─────────────┐
│ResourcePkg 1│ │ResourcePkg2 │ResourcePackageN│
└──────┬──────┘ └────┬─────┘ └──┬──────────┘
│ │ │
└───────────────┼──────────┘
│
┌───────────▼──────────┐
│ DebugPackageData │
│ ┌─────────────────┐ │
│ │ PackageName │ │
│ │ ProviderInfos[] │ │
│ │ BundleInfos[] │ │
│ │ OperationInfos[]│ │
│ └─────────────────┘ │
└─────────────────────┘
注意事项
-
DEBUG 模式自动启用
- 诊断系统仅在
DEBUG模式下启用(通过#if DEBUG条件编译) - Release 构建中不会包含诊断代码,无性能开销
- 诊断系统仅在
-
版本兼容性
- 编辑器和运行时的调试器版本必须一致
- 版本不一致的数据会被自动丢弃
-
历史数据限制
- 最多缓存 500 帧历史数据(可配置)
- 超过限制后,最早的数据会被移除
-
JSON 序列化深度限制
- Unity JsonUtility 序列化深度限制为 10 层
- 操作树(Childs)嵌套过深可能导致序列化失败
-
性能开销
- 单次采样:低开销,仅在需要时采集
- 自动采样:每帧采集,有一定性能开销,建议仅在需要时开启
-
LateUpdate 时机
- 采样在 LateUpdate 中执行,确保该帧所有操作已更新
- 避免在采样过程中资源状态发生变化
-
非序列化字典
DebugPackageData.BundleInfoDic使用[NonSerialized]标记- 字典在首次查询时才构建,减少序列化开销
-
场景追踪条件编译
SpawnScene字段仅在 DEBUG 模式下赋值([Conditional("DEBUG")])- Release 构建中该字段为空字符串
性能优化建议
-
按需采样
- 优先使用单次采样(Sample Once)
- 仅在需要连续监控时开启自动采样(Record)
-
及时关闭 Record
- 分析完成后及时关闭自动采样
- 避免不必要的性能开销
-
合理设置历史缓存
- 根据内存情况调整
MaxReportCount - 默认 500 帧已足够大多数分析场景
- 根据内存情况调整
-
导出数据离线分析
- 对于复杂的性能问题,导出 JSON 数据
- 在编辑器外使用专业工具进行分析
-
Release 构建移除诊断代码
- 确保 Release 构建使用 Release 配置
- 诊断代码通过
#if DEBUG自动移除