foreach和Stringbuilder内存探究

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值