DELPHI之关于String的内存分配(引)
在函数、过程或者方法中定义一个字符串变量时,由于我们知道在函数、过程或者方法中定义的变量为局部变量,它的内存
是在栈中分配的,但是这里有个小细节我们要注意,对于一个局部的字符串变量,它的大小为4字节,这个在栈中分配的4字
节是不是就是字符串变量的全部呢?答案是否定的,它在栈中保存的只是这个字符变量的大小和字符变量的值在堆中的内存
地址!,这就是说,该字符变量的值并不在对应的栈中,而是在堆中!我们了解到在堆上分配的内存是要程序员来处理内存
释放的,而我们在使用String类型的变量时从来就没有处理过该变量占用的内存!这是什么原因呢?这里我们就要讲到
编译器的引用计数原理了,正是因为它为我们省去了很多麻烦(当然,在某些情况我们还是小心为上,下面会提出),引用
计数其实通俗的讲就是这块内存被几个变量引用,就为这块内存计上数(可能表述不正确,但为了好记好理解故此说),是
1个就是1,有2个就是2,以此类推。当一个字符串变量使用完后,编译器自动的将其引用计数减1,如果发现引用计数为0
时,这个字符变量所占用的内存空间就被释放了。需要注意的是,什么情况下会发生引用计数呢?如果定义一个字符串变量
并给其赋值,那么这个字符串变量引用计数为1,此时假若我们再定义一个字符串变量,并将第一个字符变量的值赋值给第
二个字符变量,此时将引发引用计数,变为2,注意,编辑器此时并没有给第二个字符变量复制一份第一个字符变量的值,
而只是将第一个字符变量值的地址指针给了它!也就是说,第一个和第二个字符的值都在同一个内存块中!。但是我们通过
访问数组元素的方式访问字符变量的时候或者修改上面其中一个字符变量时,这时会引发”写复制“,之后两个字符串变量开
始使用不同的内存占用。因为所有这些对程序员来说是不必费心处理的,它全部由编译器来自动完成,那么我们在什么情况
下要小心呢?这里就和DLL有关系了,我们知道在EXE程序调用DLL时,EXE和DLL之间都是各自使用自己的内存管理器,
麻烦就出在这,假如我们在EXE中调用DLL的一个输入函数时返回一个字符串,我们知道这个字符串的引用计数为1,EXE在
使用完这个字符串后将其引用计数减1,那就是0了,按照惯例,编译器对字符串所占用的内存进行清除释放,OK,一切顺利,
那么这时该轮到DLL处理了,前面说过EXE和DLL各有各的内存管理器,这时DLL是不知道EXE已将字符串内存释放掉了,并
且还要访问那个被释放了的字符串,字符串都不存在了,访问哪儿呢?于是程序就出问题了,很显然这是我们所不希望看到的,
所在在使用DLL时,为了避免这个问题发生,我们可以有多种解决方案
1、使用内存映射文件
2、DLL单元和EXE单元都引用ShareMem单元
3、不用String类型(注:动态数组、接口和变体类型也有引用计数机制),改用PChar类型