C#使用List.Clear()方法可以讓GC回收記憶體嗎?
阿新 • • 發佈:2019-01-08
問題:我現在有一個list,裡面放了若干物件,因為業務需要,程式執行後根據使用者需求,要把list清空,再裝入新的東西,每一次用之前都會清空它,然後裝入新的東西,請問,記憶體是否會洩漏?測試:我設計一個list,清空用clear方法,裝入新東西用add(new obj)。
使用按鍵呼叫這個方法,每一次呼叫迴圈10w次。
通過工作管理員檢視記憶體狀態。剛開啟app:按下一次按鍵,執行10w次多次按下按鍵,實驗次數大於10次實驗結果表明,C#的GC對於本案例有較好的記憶體控制,第一次執行之後有記憶體上升,之後記憶體消耗非常穩定。分析,本例中,list中新增的物件為Card,每一次新增Card都是通過new的方法建立一個新的Card。Clear只是清空了list中對於這個Card物件的引用,並沒有對每一張Card本身引用做null,但是,根據GC的官方資訊,有兩方面,1、每一張new出來的card只有list可以引用,clear之後,引用計數變為0,認為是垃圾。2、每一張new出來的card從list中移除後,從程式的資料樹根就無法再次通過直接“爬樹”的方法訪問到,成為了程式資料樹中不存在,但是總資料等級表中存在的東西(new一次GC會額外做一次登記),這樣就會被認為是垃圾。
public void GameInit() { PlayerCnt = 0; PlayerList.Clear(); PileCnt = 0; Pile.Clear(); DisPileCnt = 0; DisPile.Clear(); } public void GameStrat() { GameInit(); //準備新牌堆,洗牌 PileCnt = Card.DeckLength; List<short> sort = new List<short>(); for (short i = 0; i < PileCnt; i++) { sort.Add(i);//0-207 } Shuffle(sort); for (short i = 0; i < PileCnt; i++) { Pile.Add(new Card(Card.Deck[sort[i]])); } }
使用按鍵呼叫這個方法,每一次呼叫迴圈10w次。
//menu-操作-重啟遊戲 的 事件 private void Menu_Restart_click(object sender, RoutedEventArgs e) { int i = 0; for (; i < 100000; i++) { GameInst.GameStrat(); } Console.WriteLine("Debug"+i); }
通過工作管理員檢視記憶體狀態。剛開啟app:按下一次按鍵,執行10w次多次按下按鍵,實驗次數大於10次實驗結果表明,C#的GC對於本案例有較好的記憶體控制,第一次執行之後有記憶體上升,之後記憶體消耗非常穩定。分析,本例中,list中新增的物件為Card,每一次新增Card都是通過new的方法建立一個新的Card。Clear只是清空了list中對於這個Card物件的引用,並沒有對每一張Card本身引用做null,但是,根據GC的官方資訊,有兩方面,1、每一張new出來的card只有list可以引用,clear之後,引用計數變為0,認為是垃圾。2、每一張new出來的card從list中移除後,從程式的資料樹根就無法再次通過直接“爬樹”的方法訪問到,成為了程式資料樹中不存在,但是總資料等級表中存在的東西(new一次GC會額外做一次登記),這樣就會被認為是垃圾。
新的問題來了,我這個演算法中有一個洗牌演算法,洗牌演算法原理參照我的上一篇blog
這個洗牌演算法程式碼如下:
public static void Shuffle(List<short> list)
{
int length = list.Count;
short tmp, rdtmp;
Random rd = new Random();
for (int i = 0; i < length; i++)
{
rdtmp = (short)rd.Next(i, length);
//Console.WriteLine(i + ": " + rdtmp + " ");
tmp = list[rdtmp];
list[rdtmp] = list[i];
list[i] = tmp;
}
//rd = null;
}
這個裡面有一個問題,每一次執行洗牌演算法都需要一個random物件
Random rd = new Random();
執行10w次就是new了10w次,從上面的記憶體分析可以看出,這個也被很好的控制了。
具體分析為,每一次new都生成了一個物件,然後每一次執行這段程式,rd都在不斷的指向新的random物件,之前的random物件都變成了野的物件,因此,GC也能通過上述的兩種方法發現老的random物件,實現回收,所以最後的
//rd = null;
我直接註釋掉了,因為不需要。
所以,放心的new吧。不用管記憶體回收的事情。
爽!