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


前言


今天查看一個同事的代碼,發現代碼中多處地方使用了GC.Collect()方法,我問他為什么這么做,他說感覺程序中定義了好多變量,怕GC回收不及時,用GC.Collect()可以手動掌控GC進行垃圾回收。

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

 

GC.Collect


下面我們通過一個小例子,來看一下使用GC.Collect后的內存情況。

我們知道可以通過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();
        }
    }
}

我們發現地址並沒有變化!

修改一下代碼使用for循環生成多個object:

            //創建沒有引用的垃圾對象
            for (int i = 0; i < 30000; i++)
                new object();
            //這是我們要判定地址的對象
            int[] gcTest = new int[10];

重新編譯后,執行結果如下:

地址變了!

 

通過上面的代碼,我們知道GC.Collect並不是只要執行就會進行垃圾回收,實際上GC會首先判斷當前是不是真的需要進行回收,如果內存中只有很小的垃圾(碎片化不嚴重)時,這時候啟動回收顯然得不償失,影響性能。

 

總結


1. 永遠都不要手動進行GC.Collect操作。如果你認為有,需要檢查你地代碼

2. 即使當你手動進行垃圾回收時,GC還不會立即執行,它要先判斷是否真正需要回收


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM