内存管理和垃圾回收

来源:互联网 发布:linux命令帮助 编辑:程序博客网 时间:2024/05/16 12:55

.net中的堆栈来存储类型的对象和引用类型的对象,堆栈的分配是连续的,在.Net程序中,始终存储了一个特殊的指针指向堆栈的尾部,对站上的地址从高位到地位分配内存
.net中托管堆:.Net中的引用类型的对象是分配在堆上。和堆栈一样,托管堆是进程内存空间中的一块区域。但托管堆中内存的分配却和堆栈有很大区别。受益于.NET内存管理机制,托管堆的分配是连续的。但是托管堆中存在着暂时不能被分配却已经 无用的对象内存块,当一个引用类型对象被初始化时,就会通过指向堆上可用空间的指针分配一块连续的内存,然后使堆栈上的引用指向堆上的这块内存块。而当托管堆中内存不够用,.NET开始执行垃圾回收。
.net中非托管堆:所有需要分配堆内存的非托管资源将会分配到非托管堆上。非托管的堆需要程序员手动地分配并且手动的释放,.Net的垃圾回收和内存管理制度不适用于非托管堆。
GC(Garbage Collector):是指.NET中清理托管堆上不会再被使用的对象内存,并且移动仍在被使用对象使他们紧靠在托管堆的一边,其步骤基本是:通过算法找到不再被使用的对象,移动对象使所有仍被使用的对象紧靠在托管堆的一边和调整整个状态变量。
垃圾回收机制按照对象不被使用的可能性把托管堆内的对象分为3代:0,1,2越小的代拥有越多被释放的机会,而每一次GC中仍存活的对象实例将会移到下一代。

当没有任何引用指向堆中的某个对象实例时,这个对象被试为不在被使用,l垃圾回收机制把引用分为2类
1)根引用:往往指那些静态字段使用,或者存活的局部变量的引用
2)非根引用:指那些不属于根引用,往往是对象实例中的字段
GC通过在使用的根引用遍历所有引用的对象实例,当一个对象不能被遍历,将结束一个分支不在被使用

.NET内存泄露
在一些不规范的编码情况下,即使是拥有垃圾回收和内存管理机制的托管堆上,仍然可能出现内存泄漏。内存泄露是指内存空间上产生不再被实际使用却不能分配的内存,内存泄漏将导致主机的内存随程序的运行而逐渐减少。大部分时候.NET的托管堆中存在着短暂的内存泄漏情况。那是因为对象一旦不被使用,需要等到下一个GC时才能被释放。
1)大对象的分配 .NET中所有的大对象将分配在一个特殊的区域,在回收大对象堆内的对象时,其他的大对象不会被移动,这是考虑到大规模地移动对象需要耗费过多的资源。这样在程序过多的分配和释放大对象之后,就会产生很多内存碎片。
2)不恰当地保存根引用,最简单也最常见的错误做法可能就是不恰当地把一个对象申明为公共静态变量,一个公共静态变量将一直被GC视为一个正在被使用的根引用,更糟糕的是,当这个对象内部还包含更多的对象引用时,这些对象同样不会被释放。

3)不正确的Finalize方法
Finalize方法只致力于快速而简单地释放非托管资源,并且尽可能快地返回。不正确的Finalize方法可能包含这样的代码:

a,没有保护地写日志文件
b,访问数据库
c,访问网络

d,把当前对象赋给某个存活的引用
当Finalize方法试图访问文件系统,数据库系统,或者网络时,将会有资源挣用和等潜在危险,试想一个不断尝试访问离线数据的Finalize方法,将长时间内不返回,这不仅影响了本身对象的释放,也使得排在Finalize方法队列中的所有后续对象得不到释放,这个连锁反应将很快造成内存消耗.

另一种危险代码是在Finalize方法中把对象本身又赋给另一个存活的引用,这时对象内的一部分资源已被释放,而另一部分则没有,这样一个对象被再次激活后,将导致不可预告的后果。

原创粉丝点击