最近在优化项目,发现字符串拼接的堆内存非常大,而且非常频繁。
大概原因如下
1.字符串就是char[] 长短发生变化必然要重新分配长度,以及拷贝之前的部分,产生GC
2.字符串+=进行拼接会产生装箱,生成GC
3.Append传入值类型数据,它会调用ToString方法,需要new string 然后把char[]拷进去,又会产生堆内存。
4.new StringBuidler 产生堆内存
5.StringBuidler.ToString 产生堆内存
5.string.format 它内部使用的就是StringBuilder,但是 1)new StringBuilder 2)Append 3)ToString都会产生堆内存。
所以我们需要优化的是
1.
int a = 100;
string b = a + “”;
禁止上述这种写法,会额外产生一次装箱操作。所以要采用如下写法。
string b = a.ToString();
2.少用或者不用string.format,提前缓存共享StringBuilder对象。避免用时候产生堆内存。
3.网上找到一个算法,挺有意思。提前定义好 0 – 9 之间的字符数组,如果传入值类型数据,从高位依次除以10算出每一位的数,然后再去预先声明的0-9字符数组中找对应的char,这样就就不会产生装箱GC了。
4.如果可以提前确定字符串的长度,例如,界面上显示玩家的等级, 我们可以确定level不可能超过3位数也就是100,那么可以提前声明一个长度为3的StringBuilder,通过反射取出来内部的_str,这样就可以避免最后的ToString产生的堆内存了。由于_str内容可能无法擦掉之前的所以需要调用GarbageFreeClear();方法。
1.装箱版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
usingSystem.Text;
usingUnityEngine;
usingUnityEngine.Profiling;
publicclassNewBehaviourScript:MonoBehaviour{
StringBuilderm_StringBuilder=newStringBuilder(100);
stringm_StringBuildertxt=string.Empty;
privatevoidStart()
{
m_StringBuildertxt=m_StringBuilder.GetGarbageFreeString();
}
privatevoidUpdate()
{
inti=Random.Range(0,100);
floatf=Random.Range(0.01f,200.01f);
floatd=Random.Range(0.01f,200.01f);
strings="yusong: "+i;
Profiler.BeginSample("string.format");
strings1=string.Format("{0}{1}{2}{3}",i,f,d,s);
Profiler.EndSample();
Profiler.BeginSample("+=");
strings2=i+""+f+""+d+""+s;
Profiler.EndSample();
Profiler.BeginSample("StringBuilder");
strings3=newStringBuilder().Append(i).Append(f).Append(d).Append(s).ToString();
Profiler.EndSample();
Profiler.BeginSample("StrExt.Format");
strings4=StrExt.Format("{0}{1:0.00}{2:0.00}{3}",i,f,d,s);
Profiler.EndSample();
Profiler.BeginSample("EmptyGC");
m_StringBuilder.GarbageFreeClear();
m_StringBuilder.ConcatFormat("{0}{1:0.00}{2:0.00}{3}",i,f,d,s);
strings5=m_StringBuildertxt;
Profiler.EndSample();
Debug.LogFormat("s1 : {0}",s1);
Debug.LogFormat("s2 : {0}",s2);
Debug.LogFormat("s3 : {0}",s3);
Debug.LogFormat("s4 : {0}",s4);
Debug.LogFormat("s5 : {0}",s5);
}
}
2.无装箱版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
usingSystem.Text;
usingUnityEngine;
usingUnityEngine.Profiling;
publicclassNewBehaviourScript:MonoBehaviour{
StringBuilderm_StringBuilder=newStringBuilder(100);
stringm_StringBuildertxt=string.Empty;
privatevoidStart()
{
m_StringBuildertxt=m_StringBuilder.GetGarbageFreeString();
}
privatevoidUpdate()
{
inti=Random.Range(0,100);
floatf=Random.Range(0.01f,200.01f);
floatd=Random.Range(0.01f,200.01f);
strings="yusong: "+i.ToString();
Profiler.BeginSample("string.format");
strings1=string.Format("{0}{1}{2}{3}",i.ToString(),f.ToString(),d.ToString(),s);
Profiler.EndSample();
Profiler.BeginSample("+=");
strings2=i.ToString()+f.ToString()+d.ToString()+s;
Profiler.EndSample();
Profiler.BeginSample("StringBuilder");
strings3=newStringBuilder().Append(i).Append(f).Append(d).Append(s).ToString();
Profiler.EndSample();
Profiler.BeginSample("StrExt.Format");
strings4=StrExt.Format("{0}{1:0.00}{2:0.00}{3}",i,f,d,s);
Profiler.EndSample();
Prof