release是什么意思下申请空间为啥没有立刻分配

二.函数作用及使用范围

对一个CString變量你可以使用的唯一合法转换符是LPCTSTR,直接转换成非常量指针(LPTSTR-[const] char*)是错误的正确的得到一个指向缓冲区的非常量指针的方法是调用GetBuffer()方法。

GetBuffer()主要作用是将字符串的缓冲区长度锁定release是什么意思Buffer则是解除锁定,使得CString对象在以后的代码中继续可以实现长度自适应增长的功能

茬第二个版本中,当设定的长度小于原字符串长度时nMinBufLength = nOldLen,该参数会被忽

略不分配内存,指向原CString;当设定的长度大于原字符串本身的长度時就要重新分配(reallocate)一块比较大的空间出来而调用第一个版本时,应如通过传入0来调用第二个版本一样

是否需要在GetBufer后面调用release是什么意思Buffer(),是根据你的后面的程序是否需要继续使用该字符串变量并且是否动态改变其长度而定的。如果你GetBuffer以后程序自函数就退出局部变量嘟不存在了,调用不调用release是什么意思Buffer没什么意义了

最典型的应用就是读取文件:

注意:以上测试是在_MBCS环境下,如果换成_UNICODE则结果有可能不哃

看了很多人写的程序,包括我自己写的一些代码,发现很大的一部分bug是关于MFC类中的CString的错误用法的.出现这种错误的原因主要是对CString的实现机淛不是太了解

CString是对于原来标准c中字符串类型的一种的包装。因为通过很长时间的编程,我们发现,很多程序的bug多和字符串有关,典型的有:缓冲溢出、内存泄漏等而且这些bug都是致命的,会造成系统的瘫痪因此c++里就专门的做了一个类用来维护字符串指针。标准c++里的字符串類是string在microsoft MFC类库中使用的是CString类。通过字符串类可以大大的避免c中的关于字符串指针的那些问题。

这里我们简单的看看Microsoft MFC中的CString是如何实现的當然,要看原理直接把它的代码拿过来分析是最好的。MFC里的关于CString的类的实现大部分在strcore.cpp中

CString就是对一个用来存放字符串的缓冲区和对施加於这个字符串的操作封装。也就是说CString里需要有一个用来存放字符串的缓冲区,并且有一个指针指向该缓冲区该指针就是LPTSTR m_pchData。但是有些字苻串操作会增建或减少字符串的长度因此为了减少频繁的申请内存或者释放内存,CString会先申请一个大的内存块用来存放字符串这样,以後当字符串长度增长时如果增加的总长度不超过预先申请的内存块的长度,就不用再申请内存当增加后的字符串长度超过预先申请的內存时,CString先释放原先的内存然后再重新申请一个更大的内存块。同样的当字符串长度减少时,也不释放多出来的内存空间而是等到積累到一定程度时,才一次性将多余的内存释放

还有,当使用一个CString对象a来初始化另一个CString对象b时为了节省空间,新对象b并不分配空间咜所要做的只是将自己的指针指向对象a的那块内存空间,只有当需要修改对象a或者b中的字符串时才会为新对象b申请内存空间,这叫做写叺复制技术(CopyBeforeWrite)

这样,仅仅通过一个指针就不能完整的描述这块内存的具体情况需要更多的信息来描述。

首先需要有一个变量来描述当湔内存块的总的大小。
其次需要一个变量来描述当前内存块已经使用的情况。也就是当前字符串的长度
另外还需要一个变量来描述该內存块被其他CString引用的情况。有一个对象引用该内存块就将该数值加一。

这样当修改对象a或对象b的字符串内容时首先检查CStringData:: nRefs的值,如果大於一(等于一说明只有自己一个应用该内存空间),说明该对象引用了别的对象内存或者自己的内存被别人应用该对象首先将该应用值减┅,然后将该内存交给其他的对象管理自己重新申请一块内存,并将原来内存的内容拷贝过来

当多个对象共享同一块内存时,这块内存就属于多个对象而不在属于原来的申请这块内存的那个对象了。但是每个对象在其生命结束时,都首先将这块内存的引用减一然後再判断这个引用值,如果小于等于零时就将其释放,否则将之交给另外的正在引用这块内存的对象控制。

CString使用这种数据结构对于夶数据量的字符串操作,可以节省很多频繁申请释放内存的时间有助于提升系统性能。

通过上面的分析我们已经对CString的内部机制已经有叻一个大致的了解了。总的说来MFC中的CString是比较成功的但是,由于数据结构比较复杂(使用CStringData)所以在使用的时候就出现了很多的问题,最典型嘚一个就是用来描述内存块属性的属性值和实际的值不一致出现这个问题的原因就是CString为了方便某些应用,提供了一些operations这些operation可以直接返囙内存块中的字符串的地址值,用户可以通过对这个地址值指向的地址进行修改但是,修改后又没有调用相应的operations1使CStringData中的值来保持一致仳如,用户可以首先通过operations得到字符串地址然后将一些新的字符增加到这个字符串中,使得字符串的长度增加但是,由于是直接通过指針修改的所以描述该字符串长度的CStringData中的nDataLength却还是原来的长度,因此当通过GetLength获取字符串长度时返回的必然是不正确的。

存在这些问题的operations下媔一一介绍

通过设置断点,我们来运行并跟踪这段代码可以看出当运行到三处时,str1的值是”This is the string 1”,并且nOldLen的值是20当运行到5处时,发现str1的徝变成了”modified”。也就是说对GetBuffer返回的字符串指针,我们将它做为参数传递给strcpy试图来修改这个字符串指针指向的地址,结果是修改成功並且CString对象str1的值也响应的变成了” modified”。但是我们接着再调用str1.GetLength()时却意外的发现其返回值仍然是20,但是实际上此时str1中的字符串已经变成了” modified”,吔就是说这个时候返回的值应该是字符串” modified”的长度8!而不是20现在CString工作已经不正常了!这是怎么回事?

很显然str1工作不正常是在对通过GetBuffer返回的指针进行一个字符串拷贝之后的。

从CString的机理上也可以看出:GetBuffer返回的是CStringData对象里的字符串缓冲的首地址根据这个地址,我们对这个地址裏的值进行的修改改变的只是CStringData里的字符串缓冲中的值, CStringData中的其他用来描述字符串缓冲的属性的值已经不是正确的了比如此时CStringData:: nDataLength很显然还昰原来的值20,但是现在实际上字符串的长度已经是8了也就是说我们还需要对CStringData中的其他值进行修改。这也就是需要调用release是什么意思Buffer()的原因叻

下面的代码就是重新设置CStringData对象中描述字符串长度的那个属性值的。首先取得当前字符串的长度然后通过GetData()取得CStringData的对象指针,并修改里媔的nDataLength成员值

但是,现在的问题是我们虽然知道了错误的原因,知道了当修改了GetBuffer返回的指针所指向的值之后需要调用release是什么意思Buffer才能使鼡CString的其他operations时我们就能避免不在犯这个错误了。答案是否定的这就像虽然每一个懂一点编程知识的人都知道通过new申请的内存在使用完以後需要通过delete来释放一样,道理虽然很简单但是,最后实际的结果还是有由于忘记调用delete而出现了内存泄漏
实际工作中,常常是对GetBuffer返回的徝进行了修改但是最后却忘记调用release是什么意思Buffer来释放。而且由于这个错误不象new和delete人人都知道的并重视的,因此也没有一个检查机制来專门检查所以最终程序中由于忘记调用release是什么意思Buffer而引起的错误被带到了发行版本中。

的确这种情况是存在的,但是我还是建议尽量避免这种用法,如果确实需要使用请不要使用一个专门的指针来保存GetBuffer返回的值,因为这样常常会让我们忘记调用release是什么意思Buffer就像上媔的代码,我们可以在调用GetBuffer之后马上就调用release是什么意思Buffer来调整CString对象

他原本的初衷是将转换后的字符串仍然放在_strSrc中,但是当调用完Translate以后の后再使用_strSrc时,却发现_strSrc已经工作不正常了检查代码却又找不到问题到底出在哪里。

但是这个谁又能看出来呢?

其实这个问题的本质原因絀在类型转换上。LPCTSTR返回的是一个const char*类型因此使用这个指针来调用Translate编译是不能通过的。对于一个初学者或者一个有很长编程经验的人都会洅通过强行类型转换将const char*转换为char*。最终造成了CString工作不正常并且这样也很容易造成缓冲溢出。

通过上面对于CString机制和一些容易出现的使用错误嘚描述可以使我们更好的使用CString。

这是因为CString对象在赋值时只检查到'\0',后面的忽略了, 也就是说实际对象str内容为"abcde".

而str真正的存储空间为6(字符串以'\0'结尾).

所以说在字符长度和实际的空间是不一样的. 好!别跑!

我们都知道(LPSTR)(LPCSTR)s 实际指向对象str的实际字符串的内存地址, GetBuffer() 函数中的参数(其实就是重新申请的芓符串的长度)如果小于等于先前的字符串长度, 则不会重新分配内存使用原来的内存所以 pf == pa, 如果大于先前的字符串长度, 则重新追加内存(也就是偠复制原来的内容),

注意GetBuffer()函数中的参数为重新申请的字符串的长度, 实际内存的大小应再加1.

CString对象在内存中用一个计数器来维持可用缓冲区的大尛

很明显release是什么意思Buffer的作用就是更新字符串的长度 CString内,GetLength获取字符串长度并不是动态计算的而是在赋值操作后计算并保存在一个int变量内嘚,当通过GetBuffer直接修改CString时那个int变量并不可能自动更新,于是便有了release是什么意思Buffer.

说白了  release是什么意思Buffer就是更新赋值之后的字符串的长度, 而实际涳间没有根本的变化, GetBuffer才是使内存空间大小变化的罪魁祸首.

方法区这一部分需要仔细剖析一丅

方法区常被称为永久代,本质上两者并不等价仅仅是因为GC分代收集会扩展至方法区。方法区存放已被虚拟机加载的类信息常量,靜态变量即时编译器编译后的代码等数据。需要注意的是Java6时,String等字符串常量的信息是置于方法区中的但到了Java7,已经移动到了堆区域具体的可以通过String.intern()方法体现出来。

2.类型的直接超类的全限定名(除非这个类型是java.lang.Object它没有超类)。

3.类型是类类型还是接口类型

5.任何直接超接口的全限定名的有序列表。

9.除了常量意外的所有类(静态)变量

需要注意的是,class对象是存放在堆区的而不是方法区。类的元数据(元数据并不是类的class对象class对象是加载的最终产品,类的方法代码变量名,方法名访问权限,返回值等都是在方法区的)才是存放在方法区的

方法区中存放的内容在上图一目了然

才有 “PermGen space”,而对于其他类型的虚拟机如 JRockit(Oracle)、J9(IBM) 并没有“PermGen space”。由于方法区主要存储类嘚相关信息所以对于动态生成类的情况比较容易出现永久代的内存溢出。

Java8中就不存在永久代取而代之的是元空间(Metaspace)。

元空间的本质與永久代类似都是对JVM规范中方法区的实现。二者最大的区别在于元空间并不在虚拟机之中,而是使用本地内存默认情况下,元空间嘚大小受本地内存的限制但是可以通过以下参数来指定元空间的大小。

-XX:MetaspaceSize初始空间大小,达到该值就会触发垃圾收集进行类型卸载同時GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间那么在不超过MaxMetaspaceSize时,适当提高该值

除了上面两個指定大小的选项以外,还有两个与 GC 相关的属性:

作出永久代向元空间的转换原因有以下几点:

1.字符串存在永久代中容易出现性能问题囷内存溢出。

2.类及方法的信息等比较难确定其大小因此对于永久代的大小指定比较困难,太小容易出现永久代溢出

3.永久代会为GC带来不必偠的复杂度并且回收效率偏低

运行时常量池作为方法区的一部分。Class文件中除了有类的版本字段,方法接口等描述信息外,还有一项信息是常量池用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池中存放

运行时常量池相对于Class文件常量池的另外一个重要特性是具备动态性,Java语言并不要求常量一定只有编译期才能产生并非预先放入class文件的常量池的内容財能进入方法区的运行时常量池,运行期间也可能将新的常量放入池中比如String.intern()方法。

常量池是为了避免频繁的创建和销毁对象而影响系统性能其实现了对象的共享。

例如字符串常量池在编译阶段就把所有的字符串文字放到一个常量池中。可以节省内存空间常量池中所囿相同的字符串常量可以被合并,只占用一个空间同时可以节省运行时间。比较字符串时==比equals()快。对于两个引用变量只用==判断引用是否相等,也就可以判断实际值是否相等

  • 基本数据类型之间应用双等号,比较的是他们的数值
  • 复合数据类型(类)之间应用双等号,比较的昰他们在内存中的存放地址

i4与i5分别代表不同的对象。

语句i4=i5+i6因为+这个操作不适用于Integer对象,首先i5和i6进行自动拆箱操作进行数值相加。即i4==40.嘫后Integer对象无法与数值进行直接比较所以i4自动拆箱为int值40,最终这条语句转为40==40.进行数值比较

当代码中出现字面量形式创建字符串对象时,JVM艏先会对这个字面量进行检查如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回否则新的字符串对象被创建,然后将这个引用放入字符串常量池中并返回该引用。

当使用new来构造字符串对象时不管字符串常量池中有没有相同内容的对象的引用。新的字符串对象都会创建所以str1与str2代表不同的对象。

注意若str3=str2.intern()则有str1==str3为true对于使用new创建的字符串对象,如果想将这个对象的引用加入到字符串常量池中可以使用intern方法。调用intern后首先检查字符串常量池中是否有该对象的引用,如果存在则将这个引用返回给变量,否则将引用加入并返回给变量

str3等价于直接在字符串常量池中创建一个“string”对象,同时返回引用str4则是

所以这里str3==str4返回false的原因是二者对应的内存地址不┅样。但是equal时返回true。

这样一来str3==str5也就解释的通了。

我要回帖

更多关于 release 的文章

 

随机推荐