1,AddMemoryPressure和RemoveMemoryPressure
这两个方法主要用于本地资源,比如一个位图占用了50M本地内存,但是托管对象只包含一个HBitMap(4字节或8字节)。但CRL并不知道这个内存压力,它可能允许你分配数百个位图,因为它们占用的托管内存太少了。这两个方法的目的就是要告诉GC它实际的本地资源用了多少内存量,GC知道这个信息后会调整它的垃圾回收策略,当压力变大时,他就强制执行垃圾回收。
private void MemoryPressureDemo(int size) { //创建一组对象,并指定它们的逻辑大小 for (var i = 0; i < 15; i++) { new BigNativeResource(size); } //为了演示,强制回收前面的这一组对象 GC.Collect(); GC.WaitForPendingFinalizers(); } private sealed class BigNativeResource { private int m_size; public BigNativeResource(int size) { m_size = size; if (m_size > 0) { GC.AddMemoryPressure(size);//让垃圾回收器认为对象在物理上比较大 } Console.WriteLine("BigNativeResource create"); } ~BigNativeResource() { if (m_size > 0) { GC.RemoveMemoryPressure(m_size);//让垃圾回收器认为释放了更多的内存 } Console.WriteLine("BigNativeResource destroy"); } }
测试代码如下:
private void TestFunc() { Console.WriteLine("MemoryPressureDemo size=0"); MemoryPressureDemo(0);//0导致垃圾回收器不频发 Console.WriteLine("MemoryPressureDemo size=" + (10 * 1024 * 1024)); MemoryPressureDemo(10 * 1024 * 1024);//10M 导致频繁的垃圾回收 }
结果可以看出,10M时分配一些对象后马上就在回收。
2,HandleCollector
这个也是用于本地资源的类。主要用于本地资源数量有限,如果长时间无效而未得到垃圾清理可能会导致请求失败。这个类有计数器的功能,当超过给定的初始值时,垃圾回收器就强制执行垃圾回收。
private void HandeCollectorDemo() { Console.WriteLine("HandeCollectorDemo"); for (var i = 0; i < 10; i++) { new LimitedResource(); } //出于演示,强制垃圾回收 GC.Collect(); GC.WaitForPendingFinalizers(); } private sealed class LimitedResource { //创建一个HandleCollector,告诉它有2个或更多的这样的对象存在于堆中,就执行垃圾回收 private static HandleCollector s_hc = new HandleCollector("LimitedResource", 2); public LimitedResource() { s_hc.Add();//告诉HandleCollector,堆中又增加了一个LimitedResource对象 Console.WriteLine("LimitedResource create. Count={0}", s_hc.Count); } ~LimitedResource() { s_hc.Remove();//告诉HandleCollector,已经从堆中移除了一个LimitedResource对象 Console.WriteLine("LimitedResource destroy. Count={0}", s_hc.Count); } }
执行结果:
3,System.Runtime.MemoryFailPoint类
这个类用于预测需求大量内存的操作能否成功,个人感觉很有用。它构造函数的参数是需要的内存量,单位是Mb。如果需要的内存不能请求成功,会抛出一个InsufficientMemoryException异常,这个异常继承自OutOfMemoryException异常,可以通过捕捉它来进行判断。
private void MemoryRequestDemo() { try { //逻辑上保留1.5G 的空间 using (System.Runtime.MemoryFailPoint mfp = new System.Runtime.MemoryFailPoint(1500)) { //在这里执行内存消耗大的算法 }//Dispose方法逻辑上释放这1.5G内存 } catch (InsufficientMemoryException e) { //无法保留所需的内存 Console.WriteLine(e); } }
4,编程控制相关的垃圾回收函数
- public static void Collect(); //回收全部
- public static void Collect(int generation);//回收指定的代
- public static void Collect(int generation, GCCollectionMode mode)//回收指定的代,Mode一般应选择Optimized
public enum GCCollectionMode
{
Default = 0, //目前和Forced等效,强制回收
Forced = 1, //强制回收
Optimized = 2,//只有能释放大量内存或能减少碎片化的前提下,才执行回收
}
- public static void WaitForPendingFinalizers();//挂起调用线程,直到freachable情况,完成对每个对象的Finalize方法调用。
我们经常可能看到如下代码:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
这三句的作用是:强制回收,等待Finalize执行完毕,再次强制回收(因为Finalize的那些对象只有下次垃圾回收才真正回收,所以这里要再调一次)
- RegisterForFullGCNotification,WaitForFullGCApproach,WaitForFullGCComplete,CancelFullGCNotification这几个方法是关于垃圾回收通知的,一般情况下,用的较少,可以参见MSDN的专题:http://technet.microsoft.com/zh-cn/library/cc713687.aspx
- public static int GetGeneration(object obj); //获取对象的代
- public static int GetGeneration(WeakReference wo);//获取一个弱引用对象的代
- public static long GetTotalMemory(bool forceFullCollection);//获取托管应用程序的内存大小
- public static void KeepAlive(object obj);//保持对象不被垃圾回收直到单前代码
- public static int CollectionCount(int generation);//进程启动后,取得特定代上执行的垃圾回收次数
下面是一个综合示例:
private class Genobj { ~Genobj() { Console.WriteLine("In Finalize Mehtod."); } } private void GCMehtodDemo() { Console.WriteLine("MaxGeneration={0}", GC.MaxGeneration); Genobj o = new Genobj(); Console.WriteLine("o in Gen " + GC.GetGeneration(o));//0 //force collecton GC.Collect(); Console.WriteLine("o in Gen " + GC.GetGeneration(o));//1 GC.Collect(); Console.WriteLine("o in Gen " + GC.GetGeneration(o));//2 o = null;// Console.WriteLine("collecting gen 0"); GC.Collect(0); GC.WaitForPendingFinalizers(); Console.WriteLine("collecting gen 1"); GC.Collect(1); GC.WaitForPendingFinalizers(); Console.WriteLine("collecting gen 2"); GC.Collect(2); GC.WaitForPendingFinalizers(); }
运行结果:
MaxGeneration=2 o in Gen 0 o in Gen 1 o in Gen 2 collecting gen 0 collecting gen 1 collecting gen 2 In Finalize Mehtod.
5,另外值得关注的几个类
- GCHandle:可用于手动监视对象的生命期。
- WeakReference:它实际是GCHandle的一个包装类,使用它时无需特权,它是弱引用,所有不支持GCHandleType的Normal或Pinned值。
- SafeHandle,SafeFileHandle,SafeWaitHandle,SafeBuffer,SafeRegistryHandle。
其中SafeHandle是其它几个类的抽象基类。它们主要用于与非托管代码进行安全的互操作。