68.Unity中容易产生GCAlloc的写法

68.Unity中容易产生GCAlloc的写法


68.1 题目

在 Unity 中哪些写法容易产生 GC Alloc?


68.2 深入解析

GC Alloc(垃圾回收分配)是指在堆上分配内存,这些内存后续需要垃圾回收器回收。频繁的 GC Alloc 会触发 GC,导致游戏卡顿。

容易产生 GC Alloc 的写法

1. 显式分配

直接使用 new 关键字创建引用类型对象:

// 每帧执行都会产生 GC Alloc
void Update()
{
    new MyClass();           // 分配类实例
    new List<int>();         // 分配 List
    new int[10];             // 分配数组
}

优化方案:预分配、对象池复用

2. 隐式分配

一些看似简单的操作也会产生堆分配:

操作 说明
字符串拼接 str + "text" 每次都产生新字符串
装箱 值类型转 object 产生堆分配
闭包 捕获外部变量的 lambda
委托 创建新委托实例
// 字符串拼接
string result = "Score: " + score;  // 产生新字符串

// 装箱
object obj = 123;  // int 装箱到堆

// 闭包
int x = 10;
Action action = () => Debug.Log(x);  // 捕获 x,产生闭包类

// 委托
button.onClick.AddListener(OnButtonClick);  // 每次调用都创建新委托

优化方案:使用 StringBuilder、避免装箱、缓存委托

3. 接口返回新对象

某些 Unity API 返回数组或新集合:

// 每次调用都分配新数组
Mesh.mesh.vertices    // 返回新数组
GetComponents<T>()    // 返回新数组
Physics.OverlapSphere() // 返回新数组

优化方案:缓存结果、使用非分配版本 API

// 使用非分配版本
Collider[] results = new Collider[10];
int count = Physics.OverlapSphereNonAlloc(position, radius, results);

4. 函数式/便利写法

LINQ 和某些枚举器会产生大量临时对象:

// LINQ 产生大量临时对象
var result = list.Where(x => x > 0).Select(x => x * 2).ToList();

// foreach 对某些集合产生枚举器分配
foreach (var item in dictionary.Keys) { }  // 可能分配

优化方案:热点代码避免 LINQ、使用 for 循环

5. 高频生命周期操作

频繁创建销毁对象:

// 频繁实例化销毁
void SpawnEnemy()
{
    var enemy = Instantiate(enemyPrefab);
    Destroy(enemy, 5f);  // 5秒后销毁
}

优化方案:使用对象池

GC Alloc 分类汇总

类型 示例 严重程度
显式分配 new classnew Listnew array 高(取决于频率)
隐式分配 字符串拼接、装箱、闭包、委托
API 返回 mesh.verticesGetComponents
便利写法 LINQ、匿名方法链式调用 中高
生命周期 频繁 Instantiate/Destroy

检测工具

  • Unity Profiler:GC Alloc 列显示每帧分配
  • Deep Profiling:定位具体分配位置
  • Memory Profiler:分析内存快照

68.3 答题示例

Unity 中容易产生 GC Alloc 的写法主要有五类:

第一,显式分配:new class、new List、new array,每帧执行都会在堆上分配。

第二,隐式分配:字符串拼接产生新字符串、值类型装箱、闭包捕获变量、创建委托实例。

第三,接口返回新对象:mesh.vertices、GetComponents、Physics.OverlapSphere 每次都返回新数组。

第四,便利写法:LINQ 链式调用、foreach 对某些集合产生枚举器分配。

第五,高频生命周期操作:频繁 Instantiate/Destroy。

对策:预分配、对象池、StringBuilder、避免装箱、缓存委托、用非分配版 API、热点代码避开 LINQ。


68.4 关键词联想

  • GC Alloc
  • 堆内存分配
  • 装箱
  • 字符串拼接
  • 闭包
  • 委托
  • LINQ
  • 对象池
  • Unity Profiler
  • 非分配 API


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏