string中的CopyonWrite技术

摘要:
由表及里,由感性到理性,我们先来看一看string类的Copy-On-Write的表面特征。在这里我们就看到了string的Copy-on-Write技术的影子。我们再深入的分析,在string中实现的写时拷贝,要解决两个问题:一个是内存共享,一个是Copy-on-Write。Copy-on-Write的原理:它使用的是“引用计数”,当第一个类构造时,string类的构造函数根据传入的参数在堆上分配内存,当其它类需要这块内存时,这个计数自动累加。“引用计数”就是string类的Copy-on-Write的原理。我们是使用了stringbuilder这个代理类来访问string的。
在谈这项技术之前,我们先来了解一下string类内存分配。string类有一个私有成员,其类型是一个char*,记录用户从堆上分配内存的地址,其在构造时分配内存,在析构时释放内存。因为是从堆上分配内存,所以string类在维护这块内存上是格外小心的,string类在返回这块内存地址时,只返回const char*,也就是只读的,如果你要写,你只能通过string提供的方法进行数据的改写。

由表及里,由感性到理性,我们先来看一看string类的Copy-On-Write的表面特征。我写了下面一段程序:

1string中的CopyonWrite技术第1张classProgram
2string中的CopyonWrite技术第2张string中的CopyonWrite技术第3张string中的CopyonWrite技术第4张{
3string中的CopyonWrite技术第5张staticvoidMain(string[]args)
4string中的CopyonWrite技术第6张string中的CopyonWrite技术第7张string中的CopyonWrite技术第4张{
5string中的CopyonWrite技术第5张Console.WriteLine("SharingtheMemory:");
6string中的CopyonWrite技术第5张stringstr1="HelloWorld";
7string中的CopyonWrite技术第5张stringstr2=str1;
8string中的CopyonWrite技术第5张Console.WriteLine(str1);
9string中的CopyonWrite技术第5张Console.WriteLine(str2);
10string中的CopyonWrite技术第5张
11string中的CopyonWrite技术第5张Console.WriteLine("AfterCopyonWrite:");
12string中的CopyonWrite技术第5张str1=str1.Replace('H','C');
13string中的CopyonWrite技术第5张Console.WriteLine(str1);
14string中的CopyonWrite技术第5张Console.WriteLine(str2);
15string中的CopyonWrite技术第5张
16string中的CopyonWrite技术第5张Console.ReadLine();
17string中的CopyonWrite技术第21张}
18string中的CopyonWrite技术第22张}

执行结果:Sharing the Memory:
Hello World
Hello World
After Copy on Write:
Cello World
Hello World
从执行结果我们看到,在没有修改之前,str1和str2的值是一样的,也就是说他们在内存中存放数据的地址是一样的。str1修改之后,他的值发生了变化,即存放数据的地址改变了。而str2没有发生变化。在这里我们就看到了string的Copy-on-Write技术的影子。
我们再深入的分析,在string中实现的写时拷贝,要解决两个问题:一个是内存共享,一个是Copy-on-Write。
Copy-on-Write的原理:它使用的是“引用计数”,当第一个类构造时,string类的构造函数根据传入的参数在堆上分配内存,当其它类需要这块内存时,这个计数自动累加。当灯析构时,这个计数减一,直到最后一个类析构时,这个计数为0,这时,程序才会真正释放这块内存。“引用计数”就是string类的Copy-on-Write的原理。
内存共享:如果一个类要用另一个类的数据,那就可以共享被使用类的内存了。这是很合理的,如果你不用我的,那就不用共享,只有你使用我的,才发生共享。
使用别的类的数据时,无非两种情况:(1)用别的类构造自己。(2)用别的类赋值。第一种情况是在构造函数中引用计数累加,第二种情况是在赋值操作中引用计数累加。
那么这个引用计数应该存放在哪里呢?它是存放在共享内存中的,在这儿存放着所有的引用。这样一来,所有共享一块内存区的类都有同样的一个引用计数,而这个变量的地址既然是在共享区上的,那么所有共享这块内存的类都可以访问到,也就知道这块内存的引用者有多少了。
string中的CopyonWrite技术第23张
有了这样一个机制,每当我们为string分配内存时,我们总是要多分配一个空间用来存放这个引用计数的值,只要发生拷贝构造或是赋值时,这个内存的值就会加一。而在内容修改时,string类为查看这个引用计数是否为0,如果不为零,表示有人在共享这块内存,那么自己需要先做一份拷贝,然后把引用计数减去一,再把数据拷贝过来。
就我们上面的那个例子来说:当string str1="Hello World"时,在分配内存时就多分配一个空间用来存放这个引用计数的值,当string str2=str1时,str1发现有人引用我,它就得共享自己。并将引用计数加1。当str1=str1.replace('H','C')时,它首先去查看引用计数,一看大于0,表示有人在共享,那么自己需要先做一份拷贝,然后把引用计数减去一,再把数据拷贝过来。所以,我们在修改之后,再看他们的值时,str1变了,而str2并没有变。
现在我们再来回头说说这个Copy-on-Write,我们可以把它看作是一个虚拟代理,它代理的真正的对象是共享内存,而代理完成任务就是控制要引用共享数据对象,它来判断是否有在共享这个数据,即看看引用计数是否0.若为0,表示数据没有人在共享,可以直接修改;若不为0,表示有人在共享这个数据,它就不能直接修改,需要Copy一个副本,在副本的基础上去修改。

再来看看stringbuilder
StringBuilder str = new StringBuilder(); //这里得到一个新实例
str.Append("123");
str.Append("1234");
这个时候str已经变成了1231234。
这里也使用了copy-on-write.
先把"123"copy到缓存区。 然后在write那个地方的123。
最后将它改成1231234。把新实例的地址直接赋str
其实stringbuilder是使用了一种代理的模式处理的。
我们是使用了stringbuilder这个代理类来访问string的。

免责声明:文章转载自《string中的CopyonWrite技术》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇解读ASP.NET 5 & MVC6系列(15):MvcOptions配置为什么react的组件要super(props)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C# 程序修改config文件后,不重启程序刷新配置ConfigurationManager

基本共识: ConfigurationManager 自带缓存,且不支持 写入。 如果 通过 文本写入方式 修改 配置文件,程序 无法刷新加载 最新配置。 PS. Web.config 除外:Web.config 修改后,网站会重启 (即 Web 程序 也无法在 运行时 刷新配置)。 为什么要在程序运行时,修改配置(刷新配置): > 以前C++,VB...

后台发送请求,HttpClient的post,get各种请求,带header的请求

HttpClient依赖jar包: <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</...

Simulink仿真入门到精通(十八) TLC语言

TLC(Target Language Compiler)是一种为转换为目标语言而存在的额解释性语言,其目的就是将模型中编译出来的rtw文件转换为目标代码(C/C++等)。与M语言类似,既可以写成脚本文件,也能够作为函数存在,都是解释性语言,更相似的是它们都提供具有强大功能的内建函数库。 18.1 TLC的作用 支持模型针对通用或特定目标硬件的代码生成功...

利用Java针对MySql封装的jdbc框架类 JdbcUtils 完整实现(包括增删改查、JavaBean反射原理,附源代码)

近期看老罗的视频,跟着完毕了利用Java操作MySql数据库的一个框架类JdbcUtils.java,完毕对数据库的增删改查。当中查询这块,包含普通的查询和利用反射完毕的查询,主要包含以下几个函数接口: 1、public Connection getConnection() 获得数据库的连接 2、public boolean updateByPrepar...

Redis 中 byte格式 写入、取出

实体类: package com.nf.redisDemo1.entity; import java.io.Serializable; public class News implements Serializable { private long id; private String title; private Str...

CString转string

如题,找了半天。。。 1 //CString转string 2 3 USES_CONVERSION; 4 CString temp; 5 temp = _T("kjdsaflkjdlfkj"); 6 char* s_char = W2A(temp); 7 string ss = s_char;...