字符串优化
在C#中,string是引用类型,每次动态创建一个string,c#都会在堆内存中分配一个内存用于存放字符串(包括字符串拼接、字符串分割等)。
什么地方会导致字符串GC高:
1.字符串拼接
用StringBuilder的Append代替+;
2.数字类型转为字符串产生的GC
将数字转成char[](这个char[]存下来复用),然后写入到StringBuilder中。
3.值类型在format时的拆装箱
扩展stringbuilder使其支持泛型。
4.Split导致的GC
1.如果只需要字符串的一部分,那split操作可以使用indexof找到位置,再substring,可以省点空间。
2.复杂的分隔符可以使用正则Regex.Split。
优化方案有以下几种:
1.自建缓存机制
可以用一些标志性的Key值来一一对应字符串,比如游戏项目中常用ID来构造某个字符串,伪代码如下:
1 2 3 | ResData data = GetDataById(Id); string strName = "xxxx" + data.Name; |
可以用字典将strName缓存起来,用id当key,尝试复用。
2.用C#的一些“不安全”的native方法
也就是直接使用指针来改变string中字符串的值,这样就可以重复利用string,而不需要重新分配内存。
实现:将不用的字符串用长度当key缓存起来,要申请新的字符串的时候尝试从缓存里拿并通过指针将字符数组(字符串本质上还是字符数组)改成需要的值。
3.使用StringBuilder
stringbuilder的原理是内部维护一个char数组,所有字符串的操作都是在操作这个char数组,不像string是直接建一个新的出来。
1.字符串拼接
Append():向字符数组里面加字符,默认长度是16,如果容量不够就自动扩容。java实现扩容的方式就是像List一样,复制一个二倍的出来,而C#是使用链表。
发生扩容就建一个新的StringBuilder存储该stringbuilder存不下的内容,并使用链表的方式连接。
2.ToString
倒序遍历链表拿出数组来构建string对象。
3.stringbuilder复用
Clear的实现是将length改成0,改length的操作是newLength > oldLength:扩容,并填充null。反之就是截断链表,但不会清掉其内存。
所以stringbuilder用完可以把它clear之后存起来,下次想用的时候直接拿出来用。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | StringBuilder sb = new StringBuilder(); // 使用StringBuilder对象 foreach ( var whatever in whateverlist) { sb.Append($ " {whatever}" ); } // 清除StringBuilder对象的内容 sb.Clear(); // 或者 sb.Length = 0; // 再次使用StringBuilder对象 foreach ( var whatever2 in whateverlist2) { sb.Append($ " {whatever2}" ); } |
4.AppendFormat
stringbuilder的AppendFormat参数是object,用来拼接值类型会有拆装箱的问题。
优化:扩展一个支持泛型参数的格式化追加函数AppendFormat<TP1..TPn>(),以避免垃圾回收开销。
实现:https://zhuanlan.zhihu.com/p/668253748
4.zstring
总的来说,ZString的实现原理是通过在非托管堆上申请内存并使用Span进行字符串拼接,从而避免了在托管堆上进行内存分配和回收,提高了性能。
AI答的,没用过。说是性能很好,但是有一堆限制。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了