今天查看一个同事的代码,发现代码中多处地方使用了GC.Collect()方法,我问他为何这么作,他说感受程序中定义了好多变量,怕GC回收不及时,用GC.Collect()能够手动掌控GC进行垃圾回收。性能
先不说他对GC的垃圾回收机制还不了解,就是调用GC.Collect()后GC真的会不会回收这个问题都须要再深刻了解一下。spa
下面咱们经过一个小例子,来看一下使用GC.Collect后的内存状况。code
咱们知道能够经过GCHandle设置引用类型(可直接复制到本机结构中的类型)在GC垃圾回收时不移动地址,而且获取地址值,那么就能够经过在两次地址获取中间加入Collect方法,来判断GC是否真的进行了垃圾回收。对象
using System; using System.Runtime.InteropServices; namespace TestGCCollect { class Program { static void Main(string[] args) { //建立一个没有引用的垃圾对象 new object(); //这是咱们要断定地址的对象 int[] gcTest = new int[10]; //设定Pinned通知GC在进行回收的时候不移动地址 GCHandle gcHandle1 = GCHandle.Alloc(gcTest, GCHandleType.Pinned); //获取gcTest在堆中的地址并输出 IntPtr add1 = gcHandle1.AddrOfPinnedObject(); Console.WriteLine(add1.ToString()); //通知GC当程序返回的时候能够回收 gcHandle1.Free(); //调用GC回收object垃圾 GC.Collect(); //再次获取地址 GCHandle gcHandle2 = GCHandle.Alloc(gcTest, GCHandleType.Pinned); IntPtr add2 = gcHandle2.AddrOfPinnedObject(); Console.WriteLine(add2.ToString()); gcHandle2.Free(); Console.ReadKey(); } } }
咱们发现地址并无变化!blog
修改一下代码使用for循环生成多个object:内存
//建立没有引用的垃圾对象 for (int i = 0; i < 30000; i++) new object(); //这是咱们要断定地址的对象 int[] gcTest = new int[10];
从新编译后,执行结果以下:get
地址变了!string
经过上面的代码,咱们知道GC.Collect并非只要执行就会进行垃圾回收,实际上GC会首先判断当前是否是真的须要进行回收,若是内存中只有很小的垃圾(碎片化不严重)时,这时候启动回收显然得不偿失,影响性能。it
1. 永远都不要手动进行GC.Collect操做。若是你认为有,须要检查你地代码for循环
2. 即便当你手动进行垃圾回收时,GC还不会当即执行,它要先判断是否真正须要回收