与C#内存管理对比分析
C#最大的一个改进其实就是对内存访问与管理方法的改进。在.NET中内存的管理是全权委托给垃圾回收器,由垃圾回收器来决定何时该释放内存空间。现在普遍采用两种技术来释放程序动态申请的系统内存:首先是以C++为代表的必须以手工方式使应用程序代码完成这些工作,让对象维护引用计数。然后是以.NET以及Java使用的垃圾回收器来完成内存释放工作。
在C++中让应用程序代码负责释放内存是低级、高性能的语言使用技术。这种技术非常有效,且可以让资源在不需要时就释放,因为这种技术可以直接访问内存,所以其最大的缺点是可能导致错误。而且如果程序员的记性不太好的话,也会常常忘记释放内存而导致内存泄漏。
在C#中内存的管理是依靠垃圾回收器,垃圾回收器是一个清理内存的程序。所有采用new关键字申请的动态内存空间都会分配到堆上,当.NET检测到给定过程的堆已经满时,需要清理时,就会调用垃圾回收器。垃圾回收器将采用垃圾回收算法将那些不再被引用的对象所占用的内存空间释放掉。显然由于程序员无法直接控制内存的释放,所开发出的软件性能和效率上一定会受到很大的影响。不过这种影响是随着计算机硬件技术的发展日益缩小的。
究竟是C++中直接由程序员管理内存好,还是像.NET中那样由单独一个程序来统一管理好呢?这个问题是公说公有理,婆说婆有理。但是我相信随着计算机硬件技术不断的发展、存储器空间越来越大、软件的复杂性和软件健壮性要求的不断提高,程序员直接管理内存的方式必将会退出历史舞台。当今的程序员不必再为该如何把程序分块放到容量有限的内存中运行而担心,因为这项任务已经交给了操作系统的虚拟内存来管理。相信不久将来人们也会习惯完全交由诸如垃圾回收器一类的专门程序来管理程序申请的内存空间。
在C++中内存的分配方式大致有三种:
(1) 从静态存储区域分配。内存在程序编译的时候已经分配完毕了。并且这块内存中的所有数据在程序的整个运行期间都始终存在的。例如:全局变量,static变量等等。
(2) 在栈上创建。在函数执行期间,无论什么时候到达一个特殊的执行点(左花括号)时,存储单元都可以在栈上被创建。出了执行点(右花括号),这个存储单元自动被释放。这些栈分配运算内置在处理器的指令集中,非常有效,并且不存在失败的危险,但是可供分配的内存容量很有限。
(3) 存储单元也可以从一块称为堆(也被称为自由存储单元)的地方分配,从堆(heap)上分配,亦成为动态分配。在C++中,程序在运行期间可以用malloc或者new申请任意数量的内存,程序员自己掌握释放内存的适当时机(使用free或者delete)。动态内存的生存期间是由程序员决定,使用非常灵活,但也最容易产生问题。
在C++中,我们必须非常小心第三种内存分配方式,因为内存的分配和释放都得由程序员来控制,一不小心就会出错。下面我就分析下在C++中,由于第三种内存分配方式而导致的一些常见的内存泄漏以及一系列的指针问题。
#include<iostream.h>
#include<string.h>
void GetMemory(char *p, int num)
{
p = new char[12];
}
void main()
{
char *str = NULL;
GetMemory(str,100);
strcpy(str,"hello");
}
注意到函数GetMemory(char *p,int num),中的第一个字符型指针参数。写程序的人的本意可能是希望通过此函数为str指针申请内存。但事实上却是str并不会得到所期望得到的内存,str依旧是NULL。因为函数GetMemory(char *p,int num)中所得到的只是指针str的一个副本使得p = str ,他们所存储的内容均是指向同一个内存的地址