Unity C#:静态临时变量与局部临时变量的性能差异对比
静态临时变量 vs 局部临时变量:Unity中的性能与GC对比
先看你给出的两段代码对比:
使用静态临时变量的代码
public class Shot : MonoBehaviour { static Actor tempActor; static Vector3 tempVec; static float tempFloat; static HitResult hitResult; public float damage; public float unblockable; public Vector3 originationPoint; private void OnTriggerEnter(Collider collision) { if (collision.gameObject.layer == 11) { tempActor = collision.gameObject.GetComponent<Actor>(); if (tempActor != null) { tempVec = collision.transform.position - originationPoint; // cast ray originatorActor.RayDisableColliders(); bool rayHit = Physics.Raycast(originationPoint, tempVec, out hitResult, range, 1<<11, QueryTriggerInteraction.Ignore); if (rayHit) { if (hitResult.collider.gameObject.CompareTag("Hero") || hitResult.collider.gameObject.CompareTag("Villain")) { tempActor = hitResult.collider.gameObject.GetComponent<Actor>(); tempActor.HitByShot(class_utilities.GetAngle(hitResult.transform, originationPoint), damage, unblockable); } } originatorActor.RayEnableColliders(); } } } }
使用局部临时变量的代码
public class Shot : MonoBehaviour { public float damage; public float unblockable; public Vector3 originationPoint; private void OnTriggerEnter(Collider collision) { if (collision.gameObject.layer == 11) { Actor tempActor = collision.gameObject.GetComponent<Actor>(); if (tempActor != null) { Vector3 offset = collision.transform.position - originationPoint; // cast ray originatorActor.RayDisableColliders(); HitResult hitResult; bool rayHit = Physics.Raycast(originationPoint, offset, out hitResult, range, 1<<11, QueryTriggerInteraction.Ignore); if (rayHit) { if (hitResult.collider.gameObject.CompareTag("Hero") || hitResult.collider.gameObject.CompareTag("Villain")) { tempActor = hitResult.collider.gameObject.GetComponent<Actor>(); tempActor.HitByShot(class_utilities.GetAngle(hitResult.transform, originationPoint), damage, unblockable); } } originatorActor.RayEnableColliders(); } } } }
接下来咱们直接聊核心的性能(内存分配+GC)差异,还有你需要注意的坑:
一、内存分配与GC:几乎无差异,但有本质区别
局部变量版本:
- 像
Vector3 offset、HitResult hitResult这类值类型,会直接分配在栈内存上,方法执行完毕后栈帧自动销毁,完全不会触发GC,也没有堆分配开销。 - 引用类型变量
Actor tempActor只是栈上的一个指针,你这里是通过GetComponent获取已存在的对象,没有新实例化任何对象,所以也不会产生堆内存分配。 - 哪怕是
out参数的HitResult,因为是结构体(值类型),传递的是栈上的副本,同样没有GC压力。
- 像
静态变量版本:
- 静态变量存储在堆的静态存储区,从程序启动到退出一直存在,不会被GC回收(除非手动置空且无其他引用,但这里是类的静态成员,会一直存活)。
- 每次使用只是覆盖静态变量的值,同样不会产生新的内存分配,也不会触发GC。
- 但这里有个致命的隐患:多实例冲突!如果有多个
Shot实例同时触发OnTriggerEnter(比如一堆子弹同时命中目标),静态变量会被互相覆盖——比如第一个子弹刚赋值tempActor,还没执行完逻辑,第二个子弹就把tempActor改成自己的目标,直接导致逻辑错误,这可比性能问题严重多了。另外,静态变量会一直占用内存,哪怕所有Shot都被销毁,这些变量还留在内存里,虽然单个占比小,但积少成多也会有影响。
二、CPU性能:几乎可以忽略的差异
两种写法在CPU开销上没有明显区别:不管是栈上分配局部变量,还是覆盖静态变量,都是极轻量的操作,现代CPU完全能处理,不会成为性能瓶颈。
三、可读性与维护性:局部变量版本完胜
你提到可读性重要但性能优先,但其实局部变量版本的可读性反而更好——比如把模糊的tempVec改成offset,变量名直接体现用途;而且局部变量的作用域只在方法内,不会污染类的其他区域,也不会有跨实例的干扰问题,后续维护起来更省心。
最终建议
如果只看内存分配和GC,两种写法几乎没有差异,但静态变量版本存在多实例冲突、内存长期占用的隐患,反而局部变量版本更安全、可读性更好,性能上也没损失。所以完全没必要用静态临时变量,直接采用局部变量的写法就好。
内容的提问来源于stack exchange,提问作者David Coombes
相关产品推荐
相关产品推荐

