Unity性能优化
Unity性能分为 CPU、GPU、内存、GC、IO 五大维度;卡顿、掉帧、发热、内存暴涨、闪退基本都来自这几块。
核心目标:保证每帧耗时 < 目标帧时间(60帧每帧约16.6ms;30帧每帧约33.3ms)。
一、CPU优化(主线程瓶颈最常见)
CPU开销来源:
- 引擎主线程:Transform遍历、MonoBehaviour生命周期、物理、UI、资源加载、协程、C#业务逻辑
- 物理线程 PhysX
- Job System工作线程
1. MonoBehaviour & 代码逻辑优化
- 减少
Update/LateUpdate数量 大量空的MonoBehaviour,每帧引擎也要遍历调用。
- 不每帧执行的逻辑不要写Update;用定时器、事件触发代替轮询。
- 对象休眠:不用时
enabled=false关闭脚本,不执行Update。 - 多个物体的相同逻辑,抽成管理器集中循环,而不是每个物体各自Update。
❌坏写法:上千个物体各自Update做距离检测 ✅好写法:管理器统一遍历一次列表做计算。
- 减少Transform操作
transform.position / rotation会产生C++<->C#桥接开销,频繁读写会消耗CPU。>
多次读写先缓存Transform引用:
Transform myTrans;
void Awake(){ myTrans = transform; } //缓存
- 减少
Find()、FindObjectOfType()、GetComponent()这些是遍历查找,开销大,不要放Update。 Awake/Start缓存引用;尽量用赋值、组件引用、事件传参。 - 协程 协程本身有开销,不要成千上万个协程同时跑;大量等待逻辑改用统一时间管理器。
2. 物理CPU开销
- 优先简单碰撞体Box/Sphere/Capsule,尽量避免MeshCollider动态物体。
- 减少同时活跃刚体数量;大量堆叠物体Solver开销暴涨。
- 合理使用Layer Collision Matrix关闭不需要的层碰撞。
- CCD连续碰撞检测只给高速物体开启,不要全局开。
- 静态物体不要移动Transform,会重建物理树造成卡顿。
3. UI(UGUI)CPU瓶颈
UGUI核心开销:网格重建(Canvas Rebuild)
- Canvas拆分:频繁变化UI单独一个Canvas;静态UI独立Canvas,减少整体重建。
- 尽量减少OverDraw;避免大量透明、重叠UI。
- 关闭不需要的RaycastTarget,不需要点击的图片文字取消勾选RaycastTarget。
- 不要在Update修改UI组件。
4. 资源加载CPU
禁止同步加载Resources.Load、AssetBundle.LoadFromFile放在游戏逻辑中,会阻塞主线程造成卡帧。
全部使用异步加载:Addressables / LoadFromFileAsync。
大资源分帧加载,不要同一帧批量加载大量资源。
二、GC垃圾回收优化(卡顿元凶,STW)
GC:C#托管堆垃圾回收,回收时Stop The World,主线程完全暂停,出现随机卡顿。 GC来源:频繁new临时托管对象、字符串拼接、装箱拆箱、协程迭代器分配。
- 避免循环/Update里new对象
// ❌每帧new,疯狂GC
void Update(){
List<int> temp = new List<int>();
}
// ✅复用对象池List,Clear()复用
private List<int> tempList = new List<int>();
void Update(){
tempList.Clear();
}
- 字符串不可变,
a + b会生成新字符串;高频拼接用StringBuilder。 - 避免装箱:值类型转object会分配堆内存。
例如:
Debug.Log(整数)会装箱。 - 对象池(ObjectPool) 频繁创建销毁物体(子弹、特效)不要反复Instantiate/Destroy,用对象池复用GameObject,减少GC+减少对象创建销毁开销。
- 协程会生成IEnumerator托管对象,高频创建协程产生GC。
- IL2CPP下依然有GC,不是用IL2CPP就没有垃圾回收。
工具:Profiler查看GC Alloc per Frame,定位哪里产生内存分配。
三、GPU优化(渲染瓶颈,发热、掉帧)
GPU主要指标:DrawCall、OverDraw、顶点数、纹理显存、Shader复杂度
- DrawCall(绘制调用) 每一次DrawCall是CPU提交给GPU一次绘制命令。
- UGUI自动合批;3D物体靠Static Batching静态合批、Dynamic Batching动态合批、GPU Instancing实例化。
- Static Batching:静态物体勾选Static,引擎合并网格;运行时不可移动。
- GPU Instancing:大量相同材质物体,开启Instancing,大幅降低DC(草地、怪物)。>
动态合批有顶点限制,且有开销,不适合大模型。 材质不同就无法合批,尽量复用材质实例。
- OverDraw(过度绘制) 同一个像素被多次绘制,透明物体叠加最严重。
- 减少透明粒子、半透明UI重叠;看不见的物体不要渲染。
- 使用遮挡剔除 Occlusion Culling,摄像机看不到的物体直接不提交GPU渲染。
- 纹理显存
- 纹理不要过大,2的幂次方(128,256,512,1024,2048,4096);不要随便8192超大图。
- 合理压缩纹理,不同平台使用对应压缩格式。
- Mipmap:远距离物体用更小纹理;需要开启;UI图不要开Mipmap浪费显存。
- 模型 减少顶点数量;多余UV、法线;尽量减少面片。
- Shader 移动端不要用复杂光照、多个Pass;优先URP轻量管线。
四、内存优化(容易闪退)
Unity内存分为两大部分:
- C++引擎内存:贴图、Mesh、Audio、GameObject场景对象
- C#托管堆内存:C#脚本对象,GC管理
重点坑:C#引用释放不等于引擎资源释放。
Destroy(gameObject)只销毁场景物体,贴图Mesh资源还留在内存,必须正确卸载资源。
- Resources:不要大量使用;
Resources.UnloadUnusedAssets()卸载无用资源。 - Addressables:必须配套Release,引用计数归零才释放底层资源;忘记Release内存持续上涨。
- AssetBundle:
Unload(true/false)区分;注意依赖包也要卸载。 - 不要常驻内存加载全部资源,按需加载,用完卸载。
- 避免内存泄漏:事件订阅忘记取消、静态变量持有GameObject/资源引用,资源永远不能被卸载。>
静态变量生命周期等于游戏进程,千万不要持有场景对象引用。
内存泄漏典型错误
public static Texture2D holdTex; //静态变量
void Start(){
holdTex = someTex;
}
//场景销毁,静态变量依然持有贴图引用,资源永远无法释放。
五、Profiler性能分析工具(优化前提,不要瞎猜)
Window → Analysis → Profiler
- CPU Hierarchy:看每帧CPU耗时,找到消耗最高函数
- GC Alloc:看每帧托管堆分配,定位GC来源
- Memory:查看各类内存占用
- Rendering:DrawCall、OverDraw、三角面统计
⚠️编辑器Profiler有额外开销,必须真机连接Profiler看真实性能,编辑器数据只能做参考。
六、移动端专项(安卓/iOS)
- 降低帧率:可以设置Application.targetFrameRate;手机不要无脑跑60帧。
- 关闭不必要的后处理效果;移动端慎用抗锯齿。
- 控制内存峰值,内存超手机上限直接闪退。
- IL2CPP代码剥离link.xml;减小包体。
- 大量粒子系统控制最大粒子数量,粒子会带来CPU+GPU双重压力。
七、常见误区
- ❌“换成IL2CPP就性能无敌,没有GC” IL2CPP提升C#执行速度,GC机制依然存在,照样会产生分配和卡顿。
- ❌“只要对象SetActive(false)就完全没有开销” SetActive(false)物体不会渲染,但对象本身内存还在;Awake已经执行过;不会自动释放资源。
- ❌“Destroy立刻释放内存” Destroy对象是延迟销毁,在当前帧结束才真正销毁;资源内存还要配合卸载API。
- ❌所有物体全部开Static,万事大吉 Static合批有内存开销,大量静态物体占用内存上涨。
优化优先级(从高到低)
- 先Pro Profiler定位瓶颈,不要凭感觉优化。
- 修复高频GC分配问题,解决随机卡顿。
- 降低主线程CPU开销(Update、物理、UI、大量Transform)。
- GPU优化:DrawCall、OverDraw、贴图大小。
- 内存管控,防止泄漏,按需加载卸载资源。
- 代码算法层面优化。
如果你需要,我可以整理一份Unity性能优化检查清单,或者讲解对象池简单实现。


0 条回复
还没有留言,来说点什么吧。