# 性能分析:抖音小游戏线上发热 / 卡顿 / 崩溃 > 日期:2026-09-17 > 范围:静态代码 + 资源 + 构建配置审计(无线上 PF 原始日志、无真机 Profiler) > 渠道:`sdkConfig.config.chanId = tt`(DefaultConfig / 悟空别走) > 方法:全平台内存方案七大支柱 × 全面性能工程五维(加载 / 内存 / FPS / CPU / 发热) **一句话**:中后期双口叠波没有同屏上限,战斗又全程 60fps 并用固定步长追赶表现同步,再叠加进战资源峰值与退战保留战斗包,低中端抖音机会同时出现发热、掉帧、OOM 杀进程。 --- ## 1. 观测结论(当前能确定什么) 本次**没有**线上 `PF-MEM-WARN` / `PF-THRM-FPS` 原始样本,不能把「人均告警次数」写成数字。能确定的是机制本身足以在真机上同时打出三类症状: | 症状 | 主因类型 | 峰值还是基线 | |------|----------|--------------| | 崩溃(杀进程 / 白屏 / 闪退) | GPU 纹理 + JS 节点/Spine 峰值;同屏怪无上限 | **峰值**为主,长局再叠 **基线** | | 卡顿 | 固定步长追赶 × 全量 `onCombatTick`;2 倍速再乘 | 战斗中持续,掉帧后恶化 | | 发热 | 战斗 60fps 不降、iOS 高性能模式、常亮、Box2D、Spine | 战斗相位持续 | 建议立刻用现有打点核对(见 `docs/性能打点说明.md`): - `PF-BOOT-TIER`:抖音大盘 `m*` / `q*` 分布(大量 7.3–8.5 分会落 **medium / tier1**) - `PF-MEM-WARN`:是否集中 `ph3`(战斗) - `PF-SESS-END`:退战后 `b/p/a/r` 是否回落 - `PF-THRM-FPS`:战斗是否几乎全是 `fps60` --- ## 2. 阶段图与峰值叠加点 ``` Boot 最小包 (first/comm/home + BootRes) │ kickLoad 战斗五包 zip(不可 abort) ▼ Hall 可交互 │ kickAllRemainingBundleZips + silent 大厅 UI │ 匹配页 scheduleBattleWarm → loadResPakg(BattleRes) ▼ 进战 ★峰值1 │ 大厅包未卸干净 + 战斗 zip 解压 + loadResPakg + 关面板 │ PhysicsSystem2D.enable = true ▼ COMBAT ★峰值2(随波次单调变差) │ 双口出怪、波次重叠、无 maxConcurrent │ 60fps + 30Hz 逻辑最多追 5 步/渲染帧 │ startBattleSilentDownload(popBattle / loadDir) ▼ 退战 │ tier1/3:battleRetain 钉住五包 zip,大厅仍扛战斗包 ★基线抬升 ▼ 下一局进战 ★峰值3(大厅 + 保留战斗包 + 新 load) ``` --- ## 3. 七大因素对照 | 因素 | 现状 | 对策 | 平台影响 | |------|------|------|----------| | 纹理/Spine/音频 | 52 张 max 边 >1024;剑/棍/枪合图近 2k;地图背景 1100×1667;构建有 ASTC 8×8,Spine/背景未挂 per-asset 压缩 | 拆武器 atlas、压背景、真机确认 GFX 格式 | 抖音低内存机 GPU OOM | | 加载/预载 | Boot 即并行 `kickLoad` 战斗包;开战后再 `startBattleSilentDownload` | 战斗 COMBAT 禁 silent;kickLoad 错峰到点击开始 | 进战瞬间 IO+解压发热 | | 释放/生命周期 | 退战顺序正确;tier1/3 `enableBattleResRetain=true`;战斗中告警不能卸 retain | 抖音默认关 retain,直到有 B/P/A 证据 | 大厅基线高,二次进战峰值 | | 引擎引用/UI | 关面板 destroy 成对;节点池 **无上限** | 池加 cap,长局 trim | 长局 JS+节点膨胀 | | JS 堆 | 热路径 `sort` / `new` 数组;`world.gc` 每 60 逻辑帧全扫 | 表现同步降频;有界 cache | 掉帧后 GC 尖峰 | | 逻辑/表现热路径 | 见 §4,P0 | 表现 1 次/渲染帧;同屏 cap | 发热+卡顿主因 | | 平台/容器 | iOS High Performance Mode **Plus** 打开;战斗 `setKeepScreenOn(true)`;deviceScore≥7.30 当中端 | iOS 高性能仅高档;中端按低内存策略 | 抖音 iOS 发热、Android 杀后台 | --- ## 4. P0 根因(必须先做) ### 4.1 双口叠波 + 无同屏上限(崩溃 / 卡顿 / 发热共同主因) | 项 | 证据 | |----|------| | 配表 | `Level.json` 第 18–25 波每波 **50** 只,间隔 1.6s | | 双口 | `LevelWaveParser.toWaveDefs` 每波 port1(攻 AI)+ port2(攻玩家)各一份 → **100 只/波** | | 叠波 | `BattleRuntime` 创建会话 `waveClearRequiresKillAll: false`;Const 10 = `1,8`(后续波 **8 秒**就开下一波) | | 上限 | `WaveSpawner._canSpawnMore`:`maxConcurrent` 缺省 0 = **不限**;Level 表无该字段 | 一波 50 只 × 1.6s ≈ 80s 才出完;8s 开下一波。清不掉时理论上可叠约 **10 波 × 100 ≈ 1000** 只同屏(杀怪会压下来,但中后期 2 倍速仍可到百级)。每只怪带 Spine + 运动同步 + Kinematic `RigidBody2D`。 **根因**:对抗玩法把出怪乘 2,又允许波次重叠,却没有按档位的同屏硬顶。 ### 4.2 固定步长追赶把表现同步乘最多 5 倍(卡顿正反馈 / 发热) - 逻辑 `fixedStep = 1/30`,`maxFixedIterations = 5`(`BattleSession`) - **每一步** COMBAT 都调 `CocosBattleMovementBridge.onCombatTick`:遍历全部怪物节点 `syncMonsterNode` + 全阴影 + Spine LOD - 掉到 20fps 时,一帧最多跑 5 次逻辑 + 5 次全量表现同步 → 更掉帧 → 更追赶 `tickSliceCount` 只切 ECS system,**不切** bridge。2 倍速 `TimeScale=2` 再把追赶窗口加宽。 **根因**:表现桥接绑在 `_tickOnce` 上,而不是「每渲染帧 1 次」。 ### 4.3 战斗相位强制 60fps,温控故意不降帧(发热) `thermal-profile.json.ts` 三档都是 `battleFrameRate: 60`,且: - `enablePhaseFrameRate: false` - `enableStressFrameRate: false` `ThermalGovernor` 注释写明:**禁止用降帧换发热**。内存告警只停 silent。抖音中端(deviceScore 7.30–8.50)仍满帧 + 飘字 24 + 受击 16 + 关 Spine LOD 间隔 30。 叠加: - `build-templates/bytedance-mini-game/game.json`:`enableIOSHighPerformanceMode` + **Plus** 均为 true - 战斗 `AppLifecycle.setBattleKeepScreen(true)` → 无法靠熄屏降温 **根因**:流畅策略覆盖了低中端机的热预算;iOS 高性能模式把 GPU 顶满。 ### 4.4 进战 / 退战内存峰值(崩溃) | 叠加 | 位置 | |------|------| | Boot 完成后并行 `kickLoad` 战斗分包(不可 abort) | `BootPipeline` → `PreloadPredictor.startEarlyBattleBundleDownload` | | 开战后再 `startBattleSilentDownload` | `GameController` 打开 BattleGame 成功后 | | 中端/高端退战 `enableBattleResRetain=true` 钉五包 zip | `memory-profile.json.ts` tier1/3;`GameController._enterMainReleaseBattleAndLoadHall` | | 战斗中 `onMemoryWarning` **不能**卸 retain | `MemoryDowngrade._releaseRetainedBattleRes`:`phase===Battle` 直接 return | | 节点池无 cap | `ResourcesManager.putNode` | 抖音 **≥7.30 分 = medium = tier1 = 开 retain**。这是抖音大盘主力机,不是「高端才开」。 --- ## 5. P1(放大 P0,或单独造成中度问题) | ID | 问题 | 位置 | 贡献 | |----|------|------|------| | P1-1 | `DeviceProfile.monsterSortInterval` 未接线,Y 排序仍事件触发全量 | `DeviceProfile` vs `CocosBattleMonsterPresenter.sortMonstersByY` | CPU | | P1-2 | 主线固定 `mode: 'pvp'`,双玩家槽 + `AiBattleBridge` 每帧 tick | `GameController` / `UIBattleGame.update` | CPU/内存近翻倍 | | P1-3 | `BattleRuntime.tick` 串行十余个 Host(雷公/哪吒/箭雨/长矛/被动/弓/AI…) | `BattleRuntime.ts` 783+ | CPU | | P1-4 | 每怪 Kinematic `RigidBody2D`,进战开 `PhysicsSystem2D` | `Monster.ts` / `BattlePhysicsGate` | CPU/发热 | | P1-5 | 武器皮肤+技能 **多皮肤一张 atlas**(剑 1961、约 3.18MB 源图) | `assets/res/battleGameWeapon/spine/like/...` | GPU | | P1-6 | 地图/大厅全屏背景 1100×1667,超 Tier-0 单图 1024 | `花果山背景.png` 等 | GPU | | P1-7 | `MemoryProfile.reapplyAfterProbe` **全仓库无调用**;`DeviceProfile._tier` 首次访问后锁死 | `MemoryProfile.ts` / `DeviceProfile.ts` | 档位可能偏乐观 | | P1-8 | `TargetResolver` 威胁排序比较里反复 `estimatePathLength`(含 sqrt) | `TargetResolver.ts` | CPU 尖峰 | | P1-9 | `enableConsolePfLog = true`(Release 虽 noop `console.log`,开发包/部分宿主仍有 I/O) | `sdkConfig.ts` | 次要 | --- ## 6. 五维影响(若不改) | 加载 | 内存 | FPS | CPU | 发热 | |------|------|-----|-----|------| | 首局被战斗 zip 并行下载托住 | 进战峰值 = 大厅+战斗+silent;tier1 退战基线不回落 | 掉帧后追赶使帧时更长 | 同屏实体 × 追赶次数 × Host 链 | 60fps + iOS Plus + 常亮 + Box2D | --- ## 7. 建议治理顺序(策划数值不变) > 不改出怪数量、间隔、奖励。只加**表现/内存硬顶**与**生命周期**。 ### 立刻(P0,可分 PR) 1. **同屏硬顶**:按 `DeviceCapability.quality` 设默认 `maxConcurrent`(建议 low 40 / medium 60 / high 80,含双口合计)。超限只推迟出怪,不改配表只数。 2. **表现同步每渲染帧最多 1 次**:固定步长多步时仅最后一步调 `onCombatTick`;阴影/LOD 再降频。 3. **抖音默认关 `enableBattleResRetain`**(或仅 high + 未收到过 MEM-WARN)。战斗相位告警允许 `releaseOwner('battleRetain')`。 4. **COMBAT 停止 `startBattleSilentDownload`**;Boot `kickLoad` 战斗包改为「点开始后」或更低并发。 5. **iOS**:`enableIOSHighPerformanceModePlus` 仅高档或关掉 Plus;验证发热。 ### 随后(P1) 6. low/medium 战斗目标帧 45(内存 D2 再 30);**大厅可保持 60**。这与当前「战斗不降帧」产品策略冲突,需要明确授权。 7. 接上 `monsterSortInterval`;AI 决策降频。 8. 节点池 cap + 长局 trim。 9. 拆武器合图、压 1667 背景、Spine/背景挂压缩预设。 10. 评估战斗是否必须每怪刚体(逻辑命中已存在则关物理)。 ### 回归(Release + 抖音真机,不可用编辑器代替) - 清缓存冷启动 → 立刻开战 / 大厅停 10s 再战 - 战斗 ↔ 大厅 ×10:无缺图、B/P/A 不单调涨 - 打到第 18 波以后:同屏怪数、帧时、`tt.onMemoryWarning` - 2 倍速 15 分钟:发热主观 + 是否杀进程 - 机型:deviceScore <7.3、7.3–8.5、≥8.51 各至少 1 台;iOS 开/关高性能模式对比 - 弱网进战、切后台 5s --- ## 8. 未决(需策划 / 发行 / 真机) - 同屏 cap 是否允许「推迟出怪」改变手感(数量不变,节奏变) - 战斗降到 45fps 是否可接受 - 线上 `PF-MEM-WARN` 是否集中 ph3、是否与 retain 机型重合 - 抖音包实际 ASTC 上传率(开发者工具会强制 PNG,不能当证据) --- ## 9. 相关代码入口 | 模块 | 路径 | |------|------| | 波次双口 | `assets/script/battle/config/LevelWaveParser.ts` | | 无并发上限 | `assets/mx/module/battle/unit/WaveSpawner.ts` | | 固定步长 + bridge | `assets/mx/module/battle/core/BattleSession.ts`、`CocosBattleMovementBridge.ts` | | 60fps 策略 | `assets/script/game/thermal-profile.json.ts`、`ThermalGovernor.ts` | | 内存档 / retain | `assets/script/game/memory-profile.json.ts`、`GameController.ts` | | 抖音分档 | `assets/script/game/device-capability-standards.ts` | | 降档不卸战斗 | `assets/script/game/MemoryDowngrade.ts` | | iOS 高性能 | `build-templates/bytedance-mini-game/game.json` |