Skip to content

大厂喜欢的表达

我先用工具定位,而不是凭感觉优化

标准回答 我做优化时不会先凭感觉改代码,而是先复现问题,再用工具定位瓶颈。比如卡顿我会先看 Profiler,判断是 Main ThreadGC AllocPhysicsCanvas.BuildBatch,还是 Camera.Render;如果是渲染问题,再用 Frame DebuggerRenderDoc 看 DrawCall、SetPass、Overdraw、阴影和后处理。定位清楚之后,再选择对象池、分帧、合批、异步加载、资源压缩等方案,最后用优化前后的数据证明效果。

tool-first-profiling-not-guessing

面试官想听的重点

优化不是“我觉得哪里慢”,而是:

  1. 先确认现象:卡顿、发热、内存高、加载慢、包体大。
  2. 再用工具定位:ProfilerFrame DebuggerMemory ProfilerRenderDoc
  3. 然后归类瓶颈:CPU、GPU、内存、IO、网络。
  4. 最后选择方案:对象池、异步加载、分帧、合批、压缩、裁剪。
  5. 优化后必须验证:帧率、耗时、GC、内存、DrawCall 前后对比。

加分说法

TIP

比如背包界面卡顿,我不会直接说“用对象池优化了”。我会先用 Profiler 看打开背包那一帧,发现 Canvas.BuildBatchGC Alloc 很高,说明是大量 UI 创建和布局重建导致的。然后我把 Item 改成虚拟列表,只创建可见区域,再配合对象池复用 Item。优化后打开耗时从 400ms 降到 60ms,滚动过程 GC 基本为 0。这样面试官能听出我不是只会套方案,而是能定位、分析、验证。

这个方案的代价是内存换 CPU

标准回答

这句话可以这样讲: 这个方案本质上是用内存换 CPU。比如对象池、缓存、预加载、字典索引,都是提前占用一部分内存,把运行时频繁创建、销毁、查找、计算的 CPU 开销降下来。它的收益是帧时间更稳定、GC 更少、卡顿更少;代价是内存常驻变高,管理复杂度也会增加。

memory-trades-cpu

面试加分说法

TIP

我不会只说“用了对象池所以性能更好”,我会补一句: 它的代价是内存换 CPU。比如子弹、特效、飘字这些高频对象,如果每次都 InstantiateDestroy,CPU 和 GC 压力会很明显。对象池提前把一批对象常驻在内存里,运行时只做取出、重置、归还,能减少尖峰开销。

但这个方案不是没有成本:池子太大会增加常驻内存,低端机可能有压力;对象归还时还要重置状态,避免脏数据;切场景时也要清理或按模块释放。所以我会给池子设置初始容量、最大容量、回收策略,并用 Profiler 对比优化前后的 CPU、GC 和内存数据。

我把模块边界分成数据层、逻辑层、表现层

标准回答 我在项目里会把模块边界拆成数据层、逻辑层、表现层。数据层只保存状态和配置,逻辑层负责规则校验和状态变化,表现层只负责 UI、动画、特效、音效这些显示效果。这样做的好处是:核心规则不会和界面、动画绑死,后续换 UI、改表现、做测试、定位 Bug 都更清楚。

module-boundary-data-logic-presentation

面试加分说法

CAUTION

比如背包系统里,数据层只保存 ItemId、数量、格子索引;逻辑层负责添加道具、移除道具、堆叠规则、容量判断;表现层只负责刷新格子、图标、红点和动画。 UI 不会直接改背包数组,而是发请求给逻辑层,由逻辑层修改数据,再通过事件通知 UI 刷新。

这样回答能体现什么

它能体现你不是只会写功能,而是知道怎么拆系统边界。面试官会觉得你理解: 数据不依赖表现,表现不决定规则,逻辑层才是业务核心。 这比单纯说“我做了背包、任务、技能”要强很多。

这里需要做幂等,避免重复触发

标准回答 这里我会做幂等处理,避免同一个操作因为按钮连点、事件重复派发、网络重试而重复生效。幂等的核心是:执行一次和执行多次,最终结果一致。比如领取奖励、完成任务、购买道具、触发技能伤害,都不能因为重复触发而重复发奖、重复扣血或重复扣钱。

idempotency-avoid-duplicate-trigger

面试加分说法

我一般会用几种方式保证幂等:

  1. 状态守卫:比如奖励已经领取,就直接返回,不再发奖。
  2. 唯一请求 ID:网络请求带 requestId,服务端按 ID 去重。
  3. 处理中标记:按钮点击后进入 Processing 状态,避免重复提交。
  4. 结果缓存:重复请求来了,不重新执行副作用,而是返回上一次结果。
  5. 服务端兜底:涉及货币、奖励、伤害结算,客户端只能防误触,最终必须服务端校验。

项目里可以这样说

IMPORTANT

比如任务奖励领取,我不会只在 UI 上禁用按钮,因为客户端 UI 状态不可靠。我的做法是:逻辑层先检查任务状态是否已经 Claimed,如果没领取才发奖并写入状态;如果重复触发,就直接返回已有结果。网络层也会带领取请求的唯一 ID,避免弱网重试导致重复发奖。

这个系统用配置驱动,减少硬编码

标准回答 这个系统我做成了配置驱动,核心规则不是写死在代码里,而是把数值、条件、奖励、效果、持续时间这些容易变化的内容放到配置表里。代码只负责稳定的通用流程,比如加载配置、按 ID 查询、校验条件、执行效果。这样新增技能、道具、任务、Buff 时,更多是改配置,而不是反复改代码里的 if else

config-driven-reduce-hardcoding

面试加分说法

CAUTION

比如技能系统里,我不会把每个技能都写成一个独立分支。技能的 skillId、CD、范围、伤害系数、命中特效、Buff ID 都放在配置里;代码只实现统一的释放流程:读配置、检查 CD、筛选目标、结算伤害、触发表现。 这样策划改数值或新增同类技能时,不需要程序频繁改代码,也方便热更新。

但要补充代价

配置驱动不是把所有逻辑都塞进表里。它的代价是配置会变复杂,所以必须有导表校验、字段默认值、版本兼容、错误提示和编辑器工具。否则配置错了,问题可能比硬编码更隐蔽。

这里要考虑资源生命周期和引用计数

标准回答 这里我会考虑资源生命周期和引用计数。资源不是加载完就不管了,而是要明确:谁申请、谁持有、谁释放、什么时候真正卸载。引用计数的作用是:多个模块使用同一份资源时只加载一份,每多一个使用者就 refCount++,不用时 refCount--,只有引用归零后才进入卸载流程。

resource-lifecycle-reference-counting

面试加分说法

CAUTION

比如 UI、角色和特效都用了同一张贴图,资源管理器不应该加载三份,而是缓存一份资源,并记录有几个模块在用。某个界面关闭时只释放自己的引用,不能立刻卸载资源;只有所有引用都释放后,资源才可以进入待卸载队列。

Unity 里要特别注意

业务层的引用计数不等于 Unity 引擎层一定没有引用。比如 Prefab 实例还在场景里,材质、贴图、Mesh 可能仍然被对象持有,所以不能只看 refCount == 0 就粗暴卸载。一般要先销毁实例,再释放资源句柄;如果用 Addressables,要对应 ReleaseReleaseInstance;如果是 AssetBundle,也要区分 Bundle 数据和已经加载出来的对象。

常见坑 一个是忘记 Release,导致资源常驻,内存越来越高;另一个是提前卸载,导致材质丢失、贴图变粉、对象引用丢失。 所以我一般会让资源管理器统一返回 Handle,谁申请谁释放,并在切场景、关闭 UI、对象池清理时统一检查引用计数和泄漏日志。

我会先做最小可用版本,再逐步扩展

标准回答 我做系统时会先做最小可用版本,也就是先把核心闭环跑通,再逐步扩展。第一版不追求大而全,而是先验证核心流程是否成立,比如能创建、能执行、能结束、能回收;等需求稳定后,再补扩展点、配置化、工具、性能优化和边界处理。

minimum-viable-version-then-expand

面试加分说法

IMPORTANT

比如技能系统,我不会第一版就把连招、打断、Buff、范围筛选、表现同步、网络同步全部做满。第一版先跑通释放技能、检查 CD、命中目标、结算伤害、播放表现。等这个闭环稳定后,再把技能参数配置化,再加 Buff、打断、前后摇、对象池和性能优化。

为什么这样做

这样能减少过度设计,快速验证玩法和系统边界。 但我也会提前留好接口和数据结构边界,避免第一版写成一次性代码。也就是说:实现可以先简单,但边界不能乱

我用数据验证优化效果

标准回答 我做优化时会用数据验证效果,而不是只说“感觉流畅了”。我会先记录优化前的基线数据,再定位瓶颈,实施优化后,在同设备、同场景、同版本、同操作路径下复测,最后对比优化前后的帧率、帧时间、GC、内存、加载耗时等指标。

data-validate-optimization-effect

面试加分说法

TIP

比如我优化 ScrollView 卡顿,不会只说“用了对象池”。我会先用 Profiler 记录优化前数据:打开界面耗时、滚动时 GC Alloc、Canvas.BuildBatchBehaviourUpdate 占用。优化后再用同样操作路径复测,证明 GC 从每帧 1.2MB 降到 0,打开耗时从 400ms 降到 60ms,滚动帧时间更稳定。

关键点

数据要可对比,必须控制变量。不同手机、不同场景、不同资源量测出来的数据,不能直接放一起比较。 真正有说服力的说法是:我发现了什么瓶颈,用了什么方案,优化前是多少,优化后是多少,是否有副作用,后续怎么防止问题复发。

这个问题在低端机上更明显

标准回答 这个问题在低端机上会更明显,因为低端机的 CPU、GPU、内存、带宽、IO 和散热余量都更小。高端机上可能只是轻微波动,但低端机上同样的开销就可能变成掉帧、卡顿、发热降频,甚至内存峰值过高导致闪退。

low-end-device-amplifies-performance-issue

面试加分说法

我不会只在开发机或高端机上验证优化效果。比如大量特效、透明 Overdraw、频繁 GC、同步加载资源,这些在高端机上可能不明显,但低端机更容易暴露问题。所以我会把低端机作为性能预算基准,看 P95 帧时间、峰值内存、GC Alloc、加载耗时和长时间运行后的温度、降频情况。

解决思路

NOTE

低端机上更明显的问题,一般要做分档策略: 降低特效数量、关闭高成本后处理、减少阴影距离、降低 RenderTexture 分辨率、减少同屏 AI 更新频率、压缩贴图和音频、控制常驻资源和内存峰值。 也就是说,高端机可以开更好的表现,低端机要保证稳定帧率和不闪退。

如果团队规模变大,需要补工具和规范

标准回答

这句话可以这样讲: 如果团队规模变大,我会补工具和规范,因为小团队可以靠口头沟通和个人习惯,但人多以后,协作成本、出错概率和返工成本都会上升。这个时候不能只靠“大家注意一下”,而要把经验沉淀成自动化工具、流程规范和检查规则。

team-scale-tools-and-standards

面试加分说法

NOTE

比如资源管理,团队小的时候大家口头约定贴图大小、命名规则、目录结构还勉强能跑。但团队变大后,美术、策划、程序多人并行,很容易出现重复资源、配置 ID 冲突、贴图格式错误、包体超预算、Bundle 依赖混乱。 所以我会补资源检查工具、配置导表校验、打包流水线、性能预算检查和代码 Review 清单,把容易出错的人工步骤变成自动化检查。

重点表达

工具解决“有没有做检查”,规范解决“大家按什么方式做”。 这样新人进来也能按流程稳定交付,问题尽量在提交前暴露,而不是上线后才发现。

文章评价

读完这篇,留下你的看法

暂无审核通过的评价。

登录账号后才能评价。

本站访客数0总站访问量0本页访问量0