全域性靜態集合型別的變數的垃圾你給我去死!!!!(本文知識有誤,請等待8.12晚上的反思)

銀光小子發表於2013-08-11

反思--------不怕丟臉,就怕沒收穫

原來,這篇文章居然成了個笑話唉。
事大概是這麼個事哈,我看http://bbs.csdn.net/topics/80471342,大概知道了靜態變數的回收是有限制滴。
而我剛好遇到這麼個業務,我現在想自己做一個MMO遊戲的服務端,因為服務端需要實時模擬每一個客戶端的記錄 。所以毫無疑問這個變數要是一個集合,並且一直在記憶體中。
所以我的程式碼是這麼寫的 
Public static Dictionary<int,Actor> dcActors = new Dictionary<int,Actor>();
可突發奇想,是不是靜態集合的型別的變數記憶體回收會有限制呢??於是寫了下面兩段程式碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        public static void Main(string[] args)
        {
            Run();

            Wait();
        }

        public static void Run()
        {
            B B = new B();

            for (int i = 0; i < 10000000; i++)
            {
                B.dcA.Add(i, new A());
            }
         
            B.dcA.Clear();
            Console.WriteLine("清掉了");
            GC.Collect();

        }

        public static void Wait()
        {
            Console.Read();
            Console.Read();
            Console.Read();
            Console.Read();
        }

    }

    class B
    {
        public   Dictionary<int, A> dcA = new Dictionary<int, A>();
    }



    class A
    {
        public string str = "水水水水水水水水水水水水水水水水水水水水水水水水";

     
    }


}
非靜態變數
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        public static void Main(string[] args)
        {
            Run();

            Wait();
        }

        public static void Run()
        {
            B B = new B();

            for (int i = 0; i < 10000000; i++)
            {
                B.dcA.Add(i, new A());
            }
         
            B.dcA.Clear();
            Console.WriteLine("清掉了");
            GC.Collect();

        }

        public static void Wait()
        {
            Console.Read();
            Console.Read();
            Console.Read();
            Console.Read();
        }

    }

    class B
    {
        public static  Dictionary<int, A> dcA = new Dictionary<int, A>();
    }



    class A
    {
        public string str = "水水水水水水水水水水水水水水水水水水水水水水水水";

     
    }


}
靜態變數

 

 

然後  ,非靜態變數的那段程式碼在雜湊表被清掉之後,我截圖看是這樣滴。

 

 

 

而,靜態變數的雜湊表被情調後,記憶體檢測工具顯示是這樣滴。

 

 

所以,我就信任了這個 記憶體檢測工具.....  唉.......。

 

 

而我用資源管理器裡的東西看記憶體,發現,兩個程式碼都是由600多M(填充1000W個實體類) 掉到300多M(全部清空)。

 

而我在程式碼中的型別A裡新增解構函式檢測,發現兩個程式碼在清空的時候,1000W個類A都被銷燬了

 

所以我的教訓是

1:在沒有使用到一些特殊的諸如UI,IO,資料庫,圖片,事件=....  等地方的時候,幾乎可以完全相信.NET的GC的威力。只要你沒有某些程式碼沒有死守著物件不放,GC是一定可以找到垃圾並回收的。靜態變數當然在此列

 

2:一些個工具什麼的其實未必要那麼信任。自己寫的程式碼檢測才是王道

 

3:有問題就要拿出來大家一起討論,不怕丟臉,但就怕沒收穫

 

 

 

問題

眾所周知,C#比起C++ C 等語言來說,最大的好處就是幾乎不用管理記憶體,也就是不用處理‘垃圾’,會有GC自動來清掃。但我有一個疑惑就在於,全域性靜態集合型變數的垃圾誰來收?

 

 

 

程式碼

如上圖,定義了一個  雜湊表dcA,初始給其填充   1000萬個 物件A。執行該程式,會發現1000W個物件填充到記憶體裡,記憶體會非常大,開啟資源管理器會發現,這記憶體佔了大約600M的樣子。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        public static void Main(string[] args)
        {
            Run();

            Wait();
        }

        public static void Run()
        {
        
            for (int i = 0; i < 10000000; i++)
            {
                B.dcA.Add(i, new A());
            }

            B.dcA.Clear();

            Console.WriteLine("清除了");
        }

        public static void Wait()
        {
            Console.Read();
            Console.Read();
            Console.Read();
            Console.Read();
        }

    }

    class B
    {
        public static  Dictionary<int, A> dcA = new Dictionary<int, A>();
    }

    class A
    {
        public string str = "水水水水水水水水水水水水水水水水水水水水水水水水";
    }
}

 

可接下來讓我蛋疼的問題是,清除掉了雜湊表中的這1000W個物件。記憶體......  記憶體居然還是600多M,居然一點都沒變。

 

 

 

記憶體檢測工具來了


 

 

 

圖中說的啥

第一圖非常明顯的告訴我,記憶體佔用者就是那個明明已經為空的 鍵值對大哥。一下佔了幾百兆。
第二張圖 告訴我,這個變數不是GC回收的物件,離 GC  root 還有一步之遠。

 

 

 

所以,祭出GC了

尼瑪記憶體一點都沒減少,有種可能是GC還沒出動,所以我把程式碼改一下,在  B.dcA.Clear(); 收加上  GC.Collect();
可是,這行程式碼居然一點作用都沒有。


 

 

你說,全域性靜態集合型別的靜態變數的垃圾誰來收呢??難倒一直在記憶體中呆著直到死???

感謝    imfunny 給出的一個小解決方案讓我更加了解了GC的機制。
簡單的來說我的問題就是:
假如某個變數是非靜態變數集合,比如public List<A> aList =new List<A>();  
我們給這個aList填充1千萬個物件然後清空,記憶體會馬上會被回收。
但如果是public static List<A> aList =new List<A>();  
我們給這個靜態的aList填充1千萬個物件然後清空,記憶體就絲毫不會變。
而在我的業務需求中,我要求在記憶體中在我需要的時候一直駐留著List<A>,當其中的某個A的例項不要了我清掉,
這個被我清掉的的A的例項會被回收掉。
我之前的想法是通過靜態的共有的集合型別,即public static List<A> aList =new List<A>();  
但後來發現我怎麼清空都沒用,我業務需求又註定不能用 aList=null,這樣的機制
所以,我想了一個折中的辦法,即加入這個aList屬於  B型別,那麼我再建立一個C型別,在C型別中寫一個單鍵
類,有且僅有一個B的靜態例項。這樣的話我就能實現我的業務需求了
29樓的大俠給出了我的這種思想的程式碼,各位前往圍觀啊

 


 

 

答案:


 29樓的大俠給出了我的問題的答案。各位可以參考一下 這篇部落格 http://www.cnblogs.com/bayonetxxx/archive/2009/06/02/1494728.html。即WeakReference 類的使用。嘿嘿

 

 

相關文章