我之前只知道 很多高级语言中关于字符串都有一个常量池,来保证只有一份(我记得lua语言中string就是一份)。 感兴趣的可以看看为什么众多语言都将字符串设计成不可变的?。 深入了解之后发现没有那么简单。
下面截图是自己很low的测试, 发现有意思的现象。
微软官方对字符串的文档描述:
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/constants ,下面的话解释了我上方第一个截图
https://docs.microsoft.com/zh-cn/dotnet/api/system.string.intern?view=netcore-3.1 这个就是字符串常量池 , 要注意生命周期,而且gc基本不会回收!
当我看到后面的描述我有点懵的,字符串有多份相同的拷贝?常量池的作用呢? 字符串值相等(hash值一样而已),但是两个变量的引用不等?
https://dailydotnettips.com/the-string-intern-pool/
这个文章也解答了疑惑, String.intern方法 跟 StringBuilder的ToString 返回的string引用不同的原因是什么 。
常量池副作用:
https://blog.jetbrains.com/dotnet/2015/02/12/string-interning-effective-memory-management-with-dotmemory/
dotMemory提供String重复检查。其背后的思想很简单:它会自动检查内存中具有相同值的字符串对象。打开内存快照后,您将看到以下字符串的列表:
因为原生的 interned 池子存在一个相当严重的缺点–interned字符串将“永远”保留在内存中(或者,更正确的说,它们将在AppDomain的生存期内持续存在,因为内部缓冲池将存储对字符串的引用,即使不再需要它们)。
文章中作者提出一个方式开发者自己维护一个池子
最简单的(尽管远非最佳)实现可能如下所示:
处理算法也会有所变化:
在这种情况下,ProcessLogFile完成工作后,将在下一次垃圾回收时从内存中删除池。
谈论可能的改进–例如,使用HashSet代替LocalPool的字典,更灵活的池寿命管理。另一个可能的改进–如果已处理的字符串数量巨大,则用于缓存(LRU,MRU)的相同技术。
https://blog.maartenballiauw.be/post/2016/11/15/exploring-memory-allocation-and-strings.html
文中提到的 ,说.Net垃圾回收快, 但是Unity不是的,unity的gc算法不是基于代的!
快速注:这两个使用谁比较好?:""
或string.Empty
?根据我们到目前为止所学到的知识""
,它会被扣留,这意味着它只会在内存中存储一次,对吗?对!但是有一个缺点:每次使用时""
,都会检查内部池,这会花费一些宝贵的CPU时间。如果使用string.Empty
,则将传递对象引用,这意味着不会分配额外的内存,也不会浪费额外的CPU周期检查内部缓冲池。
根据经验,请记住以下几点:
https://docs.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=netcore-3.1
重要
尽管StringBuilder类通常提供比String类更好的性能,但无论何时要操作字符串,都不应自动将String替换为StringBuilder。性能取决于字符串的大小,为新字符串分配的内存量,您的应用在其上执行的系统以及操作的类型。您应该准备测试您的应用程序,以确定StringBuilder是否确实在性能上有显着提高。
考虑在以下情况下使用String类:
IndexOf
或StartsWith
。您必须将StringBuilder对象转换为String才能执行这些操作,这会抵消使用StringBuilder带来的性能优势。有关更多信息,请参见在StringBuilder对象中搜索文本部分。考虑在以下情况下使用StringBuilder类:
StringBuilder对象 的默认容量为16个字符