学习C#,你总会遇到“托管堆”这个术语,你知道堆是什么,但托管堆又是什么呢?
为什么会有托管堆?
内存管理是一件困难的事情,如果交给程序员来处理,那么可能会因为忘记释放内存而导致内存泄漏。于是,C#替我们管理一部分内存。
托管堆是什么意思?
托管堆(Managed heap)的意思就是被C#管理的堆。
托管堆从哪里来?
托管堆是从内存空间划分出来的一个地址空间区域。
托管堆管理什么?
托管堆管理着所有引用类型指向的对象。
托管堆依靠什么管理内存?
通过垃圾回收器管理内存。垃圾回收器在托管堆中分配了太多对象的时候,会进行垃圾回收,将用不到的对象占用的空间释放(实际上更复杂)。如果垃圾回收器过于勤劳地工作,那么它就会占用CPU资源,使得程序时间效率降低。如果垃圾回收器过于懒惰,那么它不及时回收不再使用的对象,会导致内存的浪费。
托管堆不管理什么?
不管理栈上分配的值类型,不管理加载的图片资源,等等。除了引用类型的对象都不管理。
栈和托管堆
栈和托管堆都由CLR管理,如果实例化一个引用类型,那么会在托管堆中分配一个对象,然后在栈上分配一个指向该对象的指针。
托管堆和本地堆
前面说了,托管堆只管理对象,它不会管理本地堆中的资源,也不知道本地堆中被分配了这么多的资源。垃圾回收器十分专一地照顾着托管堆,当托管堆被使用了太多空间的时候会进行垃圾回收。但如果本地堆被占用了太多资源,垃圾回收器是不知道的。所以,对于如下的代码
BitMap bitmap = new Bitmap("c:\\pic.bmp");
CLR会在托管堆中创建BitMap对象,在栈中分配指向该对象的指针(图中未画出),在本地堆中再加载图片资源。
虽然,托管堆中并没有占用太多的空间,但是本地堆中却被使用了很多的空间。如果继续分配,那么本地堆的空间将被消耗殆尽,而垃圾回收器却毫无察觉。
我们可以假设
垃圾回收器是皇帝
托管堆是大臣
本地堆是百姓
皇帝(垃圾回收器)治理着国家(内存分配),但是皇帝只能从大臣(托管堆)那里得知国情(内存分配情况),如果大臣不把百姓(本地堆)的情况反应给皇帝,那么皇帝就觉得天下太平(内存空间利用率很高)。但实际情况是,百姓可能处于水生火热之中(本地堆加载了很多图片资源,大量的内存被消耗),而垃圾回收器只关注托管堆,托管堆也不告诉垃圾回收器,那么垃圾回收器丝毫不知道本地堆的内存消耗,就不会治理大臣(回收托管堆的资源,进而回收本地堆的资源)。
另一方面,大臣可以向皇帝反应国情,(通过GC.AddMemoryPressure(Int64 bytesAllocated)
函数),那么,垃圾回收器的回收机制会把本地堆的分配情况考虑在内。