字符串优化
C#字符串优化学习总结
内存区域
我们知道一个由C/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) : 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static):全局变量和静态变量的存储都是在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域, 程序结束后有系统释放 。
4、常量区:常量、字符串常量池就是放在这里的, 程序结束后由系统释放 。
5、程序代码区:存放函数体的二进制代码。
C#常量池
C#也有自己的常量池,也就是我们所称的暂存池(string intern pool),C#的字符串常量池不在堆中也不在栈中,是独立的内存空间管理,在内存的常量区,由CLR(Common Language Runtime)维护这段内存。
其中,我们定义的例如string a = "HelloWorld";
,"HelloWorld"
这个我们定义的字面量就存储在常量区中。如果再定义一个string b = "HelloWorld"
,这时候CLR就会去字符串常量池中找,如果存在相同内容的字符串对象的引用,则将这个引用返回。否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。
关于常量池的理解:
-
常量池由CLR来维护,其中的所有字符串对象的值都不相同。
-
只有编译阶段的文本字符常量会被自动添加到常量池。
-
运行时期动态创建的字符串不会被加入到常量池中。
-
string.Intern()
可以把动态创建的字符串加入到常量池中。
即使这个动态创建的字符串和常量池中的某个字符串的值相等,引用也不会相等。
即使是动态创建的两个字符串的值相等,他们的引用依然不相等。(charArray.ToString()特例)
字符串内存优化的核心原则有三个:
1、复用字符串,减少字符串数量
2、降低不可复用字符串的占用的内存
3、降低运行时产生的GC字符串内存
关于string
拼接和StringBuilder
拼接
1、在处理字符串时:string
是只可读不可写的,在进行字符串拼接时,往往是创建一个string
对象,然后栈中的内存指向堆中的新内存,在创建对象时需要分配内存空间,之前的内存则会产生GC。而StringBuilder
是存在于System.Text
命名空间下的在原来的内存中修改,不需要分配内存空间。
2、从内存优化方面来说,虽然StringBuilder
在拼接后仍需要调用ToString()
将拼接后的内容转换成不可写的字符串,但是相比较下来,频繁的字符串操作StringBuilder
更好。
3、从功能上来说string
仍然比StringBuilder
更强。
4、string
主要用于公共API,通用性好,读取性能高,占用内存小。
5、StringBuilder
主要用于拼接string
,修改性能好。
6、string
是不可变的,所以天然线程同步。
7、StringBuilder
可变,非线程同步。
如果是处理字符串的话,用string
中的方法每次都需要创建一个新的字符串对象并且分配新的内存地址,而StringBuilder
是在原来的内存里对字符串进行修改,所以在字符串处理方面还是建议用StringBuilder
这样比较节约内存。但是string
类的方法和功能仍然还是比StringBuilder
类要强。
关于string+int
string
之所以可以与int
相加,根本上是调用了Concat
方法。首先int
转object
需要装箱,然后Concat
内部调用了所有object
的ToString
方法,然后再new一个字符串返回。而Concat
方法其实接受的是object
类型的对象,这也就是说,string
在与int
相加的时候,会造成装箱操作。而ToString()
会产生28B的GC,装箱会产生20B的GC,所以在拼接时,显式的调用ToString()
可以规避掉装箱的过程,使用string+int.ToString()
会比string+int
更加节省性能
【字符串性能相关的操作】
1.创建空字符串用用string s = string.Empty
,而不是string s = ""
2.高频字符串拼接用stringbuilder
,或者字符串格式化string.Format()
而string.Format()
实际上就是利用stringbuilder
去实现的
3.ToUpper
、ToLower
这类方法均会重新生成字符串,看看是否可以避免使用
4.true判断时,用"value" == string
是最快的;false判断时,用"value".Equals(string)
是最快的