關於GC進行垃圾回收的時機

前言


今天查看一個同事的代碼,發現代碼中多處地方使用了GC.Collect()方法,我問他爲何這麼作,他說感受程序中定義了好多變量,怕GC回收不及時,用GC.Collect()能夠手動掌控GC進行垃圾回收。性能

先不說他對GC的垃圾回收機制還不瞭解,就是調用GC.Collect()後GC真的會不會回收這個問題都須要再深刻了解一下。spa

 

GC.Collect


下面咱們經過一個小例子,來看一下使用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還不會當即執行,它要先判斷是否真正須要回收

相關文章
相關標籤/搜索