.Net的StringBuilder实现在4.0和2.0区别

.Net
4.0重构了StringBuilder的实现,采用了新的数据存储方式,不仅在效率上有大的提高,并且彻底避免了中间处理过程出现临时String对象进入LOH大对象堆的情况。本文对此进行分析。

回顾.Net
2.0的StringBuilder实现

Reflector查看StringBuilder的实现:

其内部数据存储结构为string(对应成员变量m_StringValue)。StringBuilder的构造函数可以依据指定的字符串和容量来初始化,默认为空串(string.Empty),默认容量为16。构造函数使用string的GetStringForStringBuilder方法对m_StringValue变量赋值:

查看GetStringForStringBuilder的实现,m_StringValue被视为char数组,其大小由capacity容量决定,该值可以大于实际存储的string对象的大小:

在Append等方法中,若发现当前字符数组已不能满足存储要求,则调用GetNewString方法扩展,否则用wstrcpy拷贝字符到存储区域:

GetNewString采用容量翻倍的方式扩展字符数组,如果发现翻倍的扩展不能满足要求,则直接设置为requiredLength大小:

由以上分析可知,容量增长并非一定严格以16为基数增长,例如16、32、64、128,某次扩展可能导致基数发生变化。例如,假设用字符串“ABCDE”初始化一个StringBuilder,则字符数组容量为16,目前使用了其中的5个位置。随后Append一个长度为40的字符串,由于翻倍增长到32仍不能满足要求,将调整capacity=45,容量增长序列将变为16、45、90、180。

.Net
2.0 StringBuilder的容量扩展有几个缺陷:

1.
每次容量扩展都会生成一个新的string对象,其内容来自于复制老的string对象。之后,老的string对象就被废弃掉。在此过程中,若string对象大于85k(严格讲应该是85k
/ 2,因为是unicode字符),将会进入LOH大对象堆

2.
随容量的增长,例如大于1M,在LOH大对象堆中寻找连续的内存空间会变得困难,尤其若LOH碎片化严重时

3.
存在额外的拷贝消耗,会有轻微的性能损失

4.
要处理线程安全问题,包括m_StringValue本身就是被定义为volatile,也会有轻微的性能损失

可见,.Net
2.0实现的StringBuilder,内部的存储结构就是一个简单的string对象。当内部的string对象其容量不足以容纳新的Append数据时,就需要扩展容量,扩展方式为容量加倍。在容量扩展后,老的string对象及新Append的数据要拷贝到新的内存区域。这个过程中,会生成新的string对象及废弃老的string对象。如果string对象的尺寸已经大于85k,那么每次容量扩展就会有两个string对象进入到LOH中。本质上,相对于直接用“+”方式连接两个string对象,StringBuilder只是大幅降低了临时string对象的生成频率,而并不能彻底规避临时string对象的生成。

.Net
4.0新的StringBuilder实现

.Net
4.0实现StringBuilder的方式非常精彩:

存储结构不再是string对象,而明确为字符数组(对应成员变量m_ChunkChars)。每个StringBuilder字符数组的最大大小由Max_ChunkSize限定,取值为8000(对应16进制的0x1F40)。如果需要存储的数据超过8000,怎么处理呢?奥秘就在m_ChunkPrevious成员变量上,每个StringBuilder内部会维护一个指向StringBuilder实例的指针,由此构成一个StringBuilder链表。即一个逻辑上的存储,实际是被链表上所有StringBuilder实例共同分担的。

构造函数初始化m_ChunkChars的代码如下,默认字符数组大小仍为16:

容量扩展由如下方法完成:

在容量扩展时,让m_ChunkPrevious指向自己!很精彩的实现,避免了.Net
2.0时的Copy开销,仅仅就是一个指针的赋值:

而Math.Max计算决定了新字符数组的分配,其大小被控制在8000以内,从而确保不会进入LOH大对象堆。

总体看,.Net
2.0主要的实现缺陷都被优化掉了。当然,由于链表方式的引入,部分方法的处理变得复杂。例如:

不过实现同样精彩,并不需要从头到尾开始复制字符,从尾到头是高效的做法。

总结

.Net
4.0的StringBuilder实现,将原来单一String对象的存储方式,优化为一系列的内存块(Memory
Chunk)。每个StringBuilder包含一个内存块,通过引用其它StringBuilder对象的方式,形成了一个内存块的链表。通过限定每个内存块的最大大小,确保StringBuilder在容量扩展过程中不会有大对象的产生。从实现算法看,最常用的Append方法效率较.Net
2.0要高,ToString大致等效,而Insert方法效率较.Net 2.0会下降。总体看,实现相当精彩。

个人认为,StringBuilder默认容量沿用16实在太小了,常常导致最初需要多次容量扩张。如果16的容量就能满足要求,那实际上就不会用StringBuilder,而直接用String+了。个人认为512或1024是较为合适的选择。

.Net的StringBuilder实现在4.0和2.0区别,布布扣,bubuko.com

时间: 2024-10-18 02:26:06

.Net的StringBuilder实现在4.0和2.0区别的相关文章

由<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>引发的一系列问题

最近在写一个移动端的页面,起初只知道使用百分比布局就可以适应多个终端,果然,是我太年轻.当设计师甩给我一张以iphone6像素为基准的750px设计图的时候,因为这段代码<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>,开始网页布局之路,但是出现文字字体与页面图标大小不匹配的问题.一直以为

Navicat Premium 12.0.18 / 12.0.24安装与激活

若使用Navicat Premium 12.1.8.0请转至Navicat Premium 12.1.8.0安装与激活,其实每个小版本更迭变化不大.另外最重要的是,请仔细阅读本文激活部分,总有一些人遇到低级错误. 本文介绍Navicat Premium 12.0.18与Navicat Premium 12.0.24的安装.激活与基本使用. 博主将两篇博文合并,故Navicat Premium 12.0.11-12.0.18版本请看三.Navicat Premium 12.0.18激活,Navic

NVIDIA DIGITS 学习笔记(NVIDIA DIGITS-2.0 + Ubuntu 14.04 + CUDA 7.0 + cuDNN 7.0 + Caffe 0.13.0)

转自:http://blog.csdn.net/enjoyyl/article/details/47397505?from=timeline&isappinstalled=0#10006-weixin-1-52626-6b3bffd01fdde4900130bc5a2751b6d1 NVIDIA DIGITS-2.0 + Ubuntu 14.04 + CUDA 7.0 + cuDNN 7.0 + Caffe 0.13.0环境配置 引言 DIGITS简介 DIGITS特性 资源信息 说明 DIGI

REFORM-3PC.V7.0+FRNC-5PC.V8.0

Bentley.WaterGEMS.V8i.SS5.08.11.05.61 1CD 给水管网建模 REFORM-3PC.V7.0 FRNC-5PC.V8.0 通用加热炉工艺计算软件 REFORM-3PC 烃类蒸汽转化炉的炉膛截面评级程序Steam Hydrocarbon Reforming Simulation Program Chasm.Ventsim.Visual.Premium.v3.8.2.9.build.10.06.2014.Win32_64 1CD Bentley.AutoPIPE.

malloc创建三维数组,f[0],f[i][0],f[i][j][0]记录每维总数

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <malloc.h> 4 5 int main() 6 { 7 //f[0],f[][0]记录总数 8 //etc.有f[0]种等级的房间,f[i]代表第i种等级 9 //第i个等级的房间有f[i][0]间房间,f[i][j]代表第i种等级的第j个房间 10 //第f[i][j]个房间有f[i][j][0]张床 11 //f[i][j][k]为第i个等级的房间,第

大话Hadoop1.0、Hadoop2.0与Yarn平台

2016年12月14日21:37:29 Author:张明阳 博文链接:http://blog.csdn.net/a2011480169/article/details/53647012 近来这几天一直在忙于Hbase的实验,也没有太静下心来沉淀自己,今天打算写一篇关于Hadoop1.0.Hadoop2.0与Yarn的博文,从整体上把握三者之间的联系,博客内容如有问题,欢迎留言指正!OK,进入本文正题-- 在开始接触Hadoop的时候,也许大家对于Hadoop是下面的一个概念:Hadoop由两部

iOS10.0 &amp; Swift 3.0 对于升级项目的建议

iOS & Swift新旧版本更替, 在Apple WWDC大会开始之际, 也迎来了iOS 10.0, Swift 3.0 测试版, 到目前为止, 已经是测试版2.0, 每次更新都带来了新的语法变化, Swift更加简洁, 但是Xcode 越发不给力, 输出变本加厉, NSLog 一如既往的不好使, 但是,好消息是, 无论语法怎么改变, 在已有项目的基础上, Xcode更新到8.0 测试版 2.0, 您只需按照系统提示的红空心原点提示, 一个一个的替换, 就可以完美的过度到iOS 10.0,  

java中Integer i1= 0; Integer i2= 0; 为什么可以用 == 符号呢

java中Integer i1= 0; Integer i2= 0;  有i1 == i2,且有i1 = 1;那么i2为什么不会变成1呢? 我这个时候的想法是:这是因为java中整型常量是基于int的,而java中有自动包装机制,也就是说看起来i1,i2是Integer类型,但是其底层还是基于int类型的(不能理解的话在java中还有一个地方那就是Arrays.asList(T...args) 他返回一个基于数组的List,如果在List中改变其大小,那么将会引发异常[UnsupportedOp

【Web】Web1.0和Web2.0

具体请参照百度百科: web1.0时代是一个群雄并起,逐鹿网络的时代,虽然各个网站采用的手段和方法不同,但第一代互联网有诸多共同的特征,表现在技术创新主导模式.基于点击流量的盈利共通点.门户合流.明晰的主营兼营产业结构.动态网站.在WEB1.0上做出巨大贡献的公司有Netscape,Yahoo和Google. Netscape研发出第一个大规模商用的浏览器,Yahoo的杨致远提出了互联网黄页, 而Google后来居上,推出了大受欢迎的搜索服务. 中文名 web1.0 特    点 群雄并起,逐鹿