ILSPY使用指南:
http://blog.ctrlxctrlv.net/unity-decompile-dll-ilspy-for-mac/
for 与 foreach 真相
目的
在 Mono 下,研究两种不同方式的 GC 情况。
环境
同简介中的环境
测试代码
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class TestCodPerfomanceScript : MonoBehaviour
{
// Use this for initialization List<int> mData = new List<int>();
void Start()
{
mData.Add(1); //可有 n 个元素,n 也可以为 0
}
void testForForeach_foreach()
{
foreach (var e in mData)
{
//Debug.Log(e);
}
}
void testForForeach_for()
{
int len = mData.Count;
for (int i = 0; i < len; i++)
{
}
}
void testForForeach_nonforeach()
{
var e = mData.GetEnumerator();
while (e.MoveNext())
{
}
}
// Update is called once per frame
void Update()
{
//testForForeach_foreach();
//testForForeach_for(); testForForeach_nonforeach();
}
}
测试结果
数据分析
反编译->C#代码
从代码中,我们可知 foreach 中,编译器加了不少代码。从这里应该是有发生过装箱的操作。从对 testForForeach_nonforeach 函数的反编译 C#代码来看,应该是 using 那块产生装箱操作。继续查看反编译的 IL 代码。
数据结论
可以显然可知,foreach 每次有 40B 的 GC 产生(这个是 Mono 的一个 bug)。其余两种方式不产生 GC。
产生 GC 的根本原因是使用了 using 语句。(GetEnumerator()返回值类型,在
using 中装箱了)
那么,我们写一段测试代码,在去验证一下:
void testForForeach_nonforeachUsing()
{
using (var e = mData.GetEnumerator())
{
while (e.MoveNext())
{
}
}
}
得到的数据是确实是多了40B
意义
所以,在目前的项目中,foreach 还是有 GC 的。(不管是 IList 还是 ArrayList
都会有 GC)
具体见测试数据
建议项目中采用testForForeach_for()和testForForeach_nonforeach()的写法。尤其是在 update 或 LateUpdate 中。
在处理 Dictionary 遍历的时候,我们可以这样:
var enumerator = m_Dictionary.GetEnumerator();
while (enumerator.MoveNext())
{
var element = enumerator.Current; element.Value.UpdateComponent(deltaTime);
}
字符串拼接真相
目的
在 Mono 下,研究两种常用的字符串拼接的内存与 GC 问题。
环境
同简介中的环境
测试代码
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
public class TestCodPerfomanceStringBuilderScript : MonoBehaviour
{
// Use this for initialization
void Start()
{
}
void testStringBuilder_plus()
{
string test = "abc" + "efg" + "h";
int index = test.Length;
}
void testStringBuilder_StringBuilder()
{
StringBuilder testData = new StringBuilder();
testData.Append("abc");
testData.Append("efg");
testData.Append("h");
string test = testData.ToString();
}
// Update is called once per frame
void Update ()
{
testStringBuilder_plus();
//testStringBuilder_StringBuilder();
}
}
测试结果
数据结论
不同的使用场景结果是不同的,+ 号在几次连接中没有产生 GC。而在大量的连接过程中,产生大量的 GC。
意义
连接次数只有几次(10 以内),此时应该直接用 + 号连接,不产生 GC。实际上,编译器已经做了优化。
其余的使用 StringBuilder , StringBuilder 内部 Buffer 的缺省值为 16 ,按 StringBuilder 的使用场景,Buffer 肯定得重新分配。经验值一般用 256 作
为 Buffer 的初值。当然,如果能计算出最终生成字符串长度的话,则应该按这个值来设定 Buffer 的初值。使用 new StringBuilder(256) 就将 Buffer 的初始
长度设为了 256。