廢話不多說了,本人是搞Web方向的,C/S不太熟悉,先看界面圖(比較粗糙),這裡僅僅是從一個視覺的 效果來初步顯示GC相對應的操作(簡單的效果顯示,並不是真正的GC內幕,那個我也不懂)
基本概念
對象的生成過程(newobj指令)
1:計算類型(包括基類)所有字段的字節總數
2: 字節總數再加上對象開銷字段字節數(相加為:對象所需的字節數)。每個對象包含2個開銷字段 :類型對象指針以及同步塊索引。WIN32中,各占32位,WIN64中,各占64位。
3:CLR檢測托管堆中是 否有足夠的空間滿足對象所需的字節數。如果滿足,對象將被分配在NextObjPtr指針指示的地方,實例構造器 被調用,(new操作)返回對象的內存地址。指針NextObjPtr越過對象所在的區域,指示下一個新建對象在托 管堆中的地址。如果不滿足,進行垃圾收集。
根
每一個應用程序都有一組根Root。一個根是一 個存儲地址,包含一個指向類型對象的指針。
該指針有2種形式:(1)指向托管堆中的一個對象。(2 )設為null。
根包括靜態字段,方法參數,局部變量,CPU寄存器。
對象的代
托管堆中 ,對象的代大概為0代,1代,2代,相應的內存容量為256K,2M,10M。當然,垃圾收集器也會自動調整預算容 量。
終結操作和釋放模式
終結操作(Finalize()方法)可以確保托管對象在釋放內存的同時 不會洩露本地資源,但是不能確定它在何時被調用。
釋放模式(Dispose()方法):當對象不再被使用 的時候顯示的釋放掉它所占有的資源。 (更多控制)注:可以用來控制在對象生命周期內資源的重復利用, 例如connection資源不一定每次操作都要關閉。
下序的代碼顯示了GC.Collect()方法將使Finalize( )方法被調用:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> public static class Program { static void Main(string[] args) { new GCNotice(); Timer timer = new Timer(TimerCallBack,null,0,2000); Console.ReadLine(); timer.Dispose(); } private static void TimerCallBack(object obj) { Console.WriteLine("GC START Time:"+DateTime.Now.ToString()); GC.Collect(); Console.WriteLine("GC END Time:" + DateTime.Now.ToString()); } } sealed class GCNotice { ~GCNotice(){ Console.Beep(); Console.WriteLine("*********GCNotice FINALIZE():"+DateTime.Now.ToString()); if(!AppDomain.CurrentDomain.IsFinalizingForUnload()) { new GCNotice(); } } }
~GCNotice(){
} 析構函數(C++)就是我們所說的終結操作(與C++不同),也 就是Finalize()方法。在下列事件中將觸發:
(1):第0代對象充滿時(垃圾收集)。
(2) :代碼顯示調用System.GC.Collect()。
(3):Windoms報告內存不足。
(4):CLR卸載應用 程序域。
(5):CLR關閉。
一般情況下,如果一個類型中本地資源需求比較大,建議使用 HandleCollector來促進GC.Collect()執行(釋放資源)。
代碼 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->namespace System.Runtime.InteropServices { // 摘要: // 跟蹤未處理的句柄,並在達到指定阈值時強制執行垃圾回收。 public sealed class HandleCollector { // 摘要: // 使用一個名稱以及一個阈值(在達到該值時開始執行句柄回收)初始化 System.Runtime.InteropServices.HandleCollector // 類的新實例。 // // 參數: // name: // 回收器的名稱。此參數允許您為跟蹤句柄類型的回收器分別命名。 // // initialThreshold: // 指定何時開始執行回收的值。 // [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public HandleCollector(string name, int initialThreshold); // 摘要: // 增加當前句柄計數。 public void Add(); // 減少當前句柄計數。 // public void Remove(); } }
public HandleCollector(string name, int initialThreshold); //組合到類型 中,實例化
public void Add();//構造函數中運用
public void Remove();//析構函數中運用
終結鏈表與終結可達隊列
創建一個新對象時,如果對象的類型定義了Finalize()方法,那麼指 向該對象的指針將被放到終結鏈表中。終結鏈表上的每一個條目都引用著一個對象,指示GC在回收這些對象之 前調用它們的Finalize()方法。
主要過程如下
好了,概念的東西不再介紹了,本人思路如下:
(一)准備工作:創建一個DataObject類型(模擬一個對象實體),DataObjects(對象集合), DataObjectManager(對象集合管理)。
(二)初始化一個屬性值為隨機值的 DataObject對象
(三)判斷托管堆0代內存是否充足,如果滿足則分配對象內存(模擬)(如果有終結方法,則添加引用到終 結鏈表中)。如果不滿足,進行垃圾收集。
(四)垃圾收集操作:細分為0,1,2代的比較判斷與操作
(五)收集後內容的顯示,調用面板panel的refresh()方法。
(六)隨機修改原對象集合中的 對象的值 HasRoot為false(後來添加的),標識無根。
(一) 准備工作
先自創建一個類,主 要是以該對象來作為操作的。
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> public class DataObject : Jasen.GCShow.IDataObject { public Boolean HasFinalizeMethod { get; set; } public Boolean HasRoot { get; set; } public Int32 Number { get ;set; } public System.Drawing.Color Color { get;set; } public String OXString{ get{ //return (HasRoot ? "R" : "0") + (HasFinalizeMethod ? "F" : ""); return (HasFinalizeMethod ? "[F]" : "[!F]"); } } public String Name { get; set; } public String NiceName { get; set; } public Int32 Generation { get; set; } }
然後就是該類對象集合,實現遍歷以及索引:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> public class DataObjects : IEnumerable, Jasen.GCShow.IDataObjects { private List<DataObject> objectList=null; public DataObjects(List<DataObject> objects) { this.objectList = objects; } public DataObjects(){ this.objectList=new List<DataObject>(); } public void Add(DataObject item) { if(!objectList.Contains(item)){ objectList.Add(item); } } public void Remove(DataObject item) { if (objectList.Contains(item)){ objectList.Remove(item); } } public int Count() { if(objectList==null){ return 0; } return objectList.Count; } public DataObject this[int i]{ get{ if (objectList != null && objectList.Contains(objectList[i])){ return objectList[i]; } else{ return default(DataObject); } } set{ objectList[i] = value; } } #region IEnumerable 成員 IEnumerator IEnumerable.GetEnumerator() { for (int i = 0; i < objectList.Count(); i++) { yield return this[i]; } } #endregion }
其次就是該對象集合的管理類,負責所有對象的ItemCollection,以及0,1,2代對象集合,以 及終結鏈表,終結可達隊列
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 public class DataObjectManager : Jasen.GCShow.IDataObjectManager { DataObjects items = new DataObjects(); Queue<DataObject> freachableQueue = new Queue<DataObject>(); DataObjects finalizeTable = new DataObjects(); public DataObjects ItemsCollection { get { return items; } set { items = value; } } public DataObjects ZeroGenerationCollection { get { return GetCollection(0); } } public DataObjects GetCollection(int generation) { if (ItemsCollection.Count() == 0) return null; DataObjects generationObjects = new DataObjects(); foreach(DataObject obj in ItemsCollection){ if(obj.Generation==generation){ generationObjects.Add(obj); } } return generationObjects; } public DataObjects OneGenerationCollection { get { return GetCollection(1); } } public DataObjects TwoGenerationCollection { get { return GetCollection(2); } } public DataObjects FinalizeTable { get { return finalizeTable; } set { finalizeTable = value; } } public Queue<DataObject> FreachableQueue { get { return freachableQueue; } set { freachableQueue = value; } } }
(二)初始化一個屬性值為隨機值的 DataObject對象
通過隨機設置類的值來實例化一 個對象
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->DataObject item = new DataObject() { HasFinalizeMethod = Randoms.GetRandomBoolen(0.3), HasRoot = Randoms.GetRandomBoolen(0.6), Color = Randoms.GetRandomColor(), Number = Randoms.RandomNum(1, 3), Name = Guid.NewGuid().ToString(), NiceName = Randoms.AddNum().ToString(), Generation = 0 // 默認為0代 };
以上的值大部分是隨機的,不確定的,比如下面的方法----->返回隨機比例為 rate(比如0.3)的true值,它等價於有30%的概率返回true,70%概率返回false,
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> /// <summary> /// 返回隨機比例為rate的 true值 /// </summary> /// <param name="rate"></param> /// <returns></returns> public static Boolean GetRandomBoolen(double rate) { if (rate < 0 || rate > 1) throw new ArgumentOutOfRangeException("rate must be between 0 to 1"); Random random = new Random((int)DateTime.Now.Ticks); System.Threading.Thread.Sleep(100); if(random.Next(0,10000)>=10000*(1-rate)){ return true; } return false; }
隨機顏色如下
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> public static Color GetRandomColor() { Random randomFirst = new Random((int)DateTime.Now.Ticks); System.Threading.Thread.Sleep(300); Random randomSencond = new Random((int)DateTime.Now.Ticks); System.Threading.Thread.Sleep(300); Random randomThird = new Random((int)DateTime.Now.Ticks); int intRed = randomFirst.Next(256); int intGreen = randomSencond.Next(256); int intBlue = randomThird.Next(256); return Color.FromArgb(intRed, intGreen, intBlue); }
(三)判斷托管堆0代內存是否充足
判斷的大概過程如下:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> #region newobject指令過程 private Int32 CHARTOTALNUMBER = 0; private void NewObjectOperationProcess(DataObject item){ //計算類型所有字段的字節總數 CHARTOTALNUMBER = CountTypeCharTotalNumber(item); //計算2個開銷字段:類型對象指針,同步塊索引 WIN32--32位×2=64位=8字節 CountObjectExtraCharNumber(); //判斷0代對象內存(256K)是否含有所需的字節數 (長度) Boolean isEnough= CheckZeroGenerationHasEnoughChars(); //計算新建對象在托管堆中的地址 (長度) if (isEnough) { RefreshZeroGenenrationAndFinalizeTable(item); } else { //回收垃圾 GCCollect(item); } }
如果托管堆0代內存充足,那麼顯示如下:
上面顯示的是對象含有根,沒有終結方法。我們來看一張含有終結方法的圖,含有終結方法的對 象會被添加引用到終結鏈表中,如下:
(四)垃圾收集操作:細分為0,1,2代的比較判斷與操作
(1)處理托管堆0代對象的主 要操作如下:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void GCSystemOperation() { ClearFreachableQueue(); DataObjects temps = new DataObjects(); //清理沒根的沒終結方法的0代對象 0代對象 +1 (清除) DataObjects list = manager.ZeroGenerationCollection; if (list == null) return; foreach (DataObject obj in list) { //如果對象沒有根 並且沒有終結方法 if (obj.HasRoot == false && obj.HasFinalizeMethod == false){ manager.ItemsCollection.Remove(obj); } else { temps.Add(obj); //obj.Generation++; } } if(temps.Count()>0){ int tempsLength=CountSize(temps); int oneGenerationCurrentLength = CountSize(manager.OneGenerationCollection); Boolean isOneGenerationEnough = (SystemConst.OneGenerationLength- oneGenerationCurrentLength > tempsLength)?true:false; if (isOneGenerationEnough) { GenerationAddOne(temps); } else { //處理托管堆1代對象 MessageBox.Show("處理托管堆1代對象!"); HandleOneGeneration(temps); } } }
當一直添加對象時,達到如下情況:
我們不知道下一個對象的內存大小,很有下一次就會可能發生垃圾收集。如下圖所示,當托管堆0 代對象內存容量不足時,會觸發垃圾收集:
其中先清理可達隊列中的數據對象,(含有Finalize()終結方法並且無根,一般情況為在第1次收 集時將終結鏈表中的指針移動至終結可達隊列中,這樣可達隊列中才有指針。第2次收集就會將可達隊列中的 指針清理)
執行下列代碼:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 private void ClearFreachableQueue() { //清理終結可達隊列中的對象 沒根 有終結方法 (清除) 一般為清理上次收集數據 while (manager.FreachableQueue.Count > 0){ DataObject obj = manager.FreachableQueue.Dequeue(); manager.ItemsCollection.Remove(obj); } MessageBox.Show("清理可達隊列對象"); //終結鏈表中的數據 --》可達隊列 foreach (DataObject item in manager.FinalizeTable){ if (item.HasRoot == false){ manager.FreachableQueue.Enqueue(item); } } MessageBox.Show("將終結鏈表中的可達對象移動至可達隊列"); foreach (DataObject obj in manager.FreachableQueue){ manager.FinalizeTable.Remove(obj); } MessageBox.Show("移除終結鏈表中包含的可達隊列對象"); }
顯然,將終結鏈表的數據移動到可達隊列後,然後再移除終結鏈表包含的可達隊列的指針 ,操作後如下:
(2)處理托管堆1代對象的主要操作如下:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void HandleOneGeneration(DataObjects temps) { DataObjects currentTempObjects = new DataObjects(); foreach(DataObject obj in manager.OneGenerationCollection){ if (obj.HasRoot == false && obj.HasFinalizeMethod == false){ manager.ItemsCollection.Remove(obj); } else { currentTempObjects.Add(obj); } } if (currentTempObjects.Count() > 0) { Boolean enough = CheckTwoGenerationEnough(currentTempObjects); if (enough) { MessageBox.Show("托管堆2代內存充足----》托管堆1代對象 對象代+1"); GenerationAddOne(currentTempObjects); } else { MessageBox.Show("托管堆2代內存不足----》處理"); HandleTwoGeneration(currentTempObjects); } } MessageBox.Show("托管堆0代對象 對象代+1"); GenerationAddOne(temps); }
繼續創建新的對象:
發現越來越多的對象在托管堆1代中存在。
一直增加,當托管堆0代對象內存不足,並且托管堆1代對象內存也不足時候,將導致1代對象的代 +1;其中也包括1代對象的清理工作,移除無根的對象。
(3)處理托管堆2代對象的主要操作如下:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 private void HandleTwoGeneration(DataObjects currentTempObjects) { Boolean enough = CheckTwoGenerationEnough(currentTempObjects); if (enough){ GenerationAddOne(currentTempObjects); } else { MessageBox.Show("托管堆2代對象內存滿了,清理托管堆2代無根對象"); ClearGenerationUnusefulObject(manager.TwoGenerationCollection); if (CheckGenerationEnough(currentTempObjects, manager.TwoGenerationCollection, SystemConst.TwoGenerationLength)){ MessageBox.Show("托管堆1代對象 對象代+1"); GenerationAddOne(currentTempObjects); } else{ ClearToEmpty(); //托管堆對象全部清理 } } }
(五)垃圾收集後內容的顯示,調用面板panel的refresh()方法。
例如托管堆0代 的面板刷新
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void panelZeroGenenration_Paint(object sender, PaintEventArgs e) { if (manager.ItemsCollection.Count() == 0) { return; } DataObjects list = manager.ZeroGenerationCollection; if(list==null)return; Graphics graphics = e.Graphics; FillRectangle(graphics, list, true,true ); }
相應的面板繪制如下,采用累加來計算繪制的坐標(left)
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 private void FillRectangle(Graphics graphic, DataObjects list,Boolean markArrows,Boolean markOX) { float left = 0,width = 0,top = 0,height = 20,rams=-15; for (int i = 0; i < list.Count(); i++){ int sum = 0; if (i != 0) { for (int z = 0; z < i; z++){ sum += list[z].Number;//i-1次 } } left = sum * SystemConst.GraphicLength; width = SystemConst.GraphicLength * list[i].Number; graphic.FillRectangle(new SolidBrush(list[i].Color), left, top, width, height); graphic.FillRectangle(new SolidBrush(Color.Red), left, top, 2, height); graphic.DrawString(("[" + list[i].NiceName + "]" + (list[i].HasRoot ? "R" : "")), smallFont, new SolidBrush(Color.Red), left, top); if(markOX){ graphic.DrawString(list[i].OXString, smallFont, new SolidBrush(Color.Red), left, top + 40); } } if(markArrows){ graphic.DrawString("↑", bigFont, new SolidBrush(Color.Red), left + width+rams, top + 20); } }
(六)隨機修改原對象集合中的對象的值 HasRoot為false(後來添加的),標識無根。
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 /// <summary> /// 隨機修改對象的根為 false /// </summary> private void RandomChangeItemsRootValue() { DataObjects list = manager.ItemsCollection; if (list == null) return; foreach (DataObject item in list){ if (item.HasRoot == true){ item.HasRoot = Randoms.GetRandomBoolen(0.9); } } }
最後顯示如下(托管堆0代對象+1,1代對象+1):
同時我們應該注意到:在第6步中的方法隨機的修改了集合中對象的HasRoot屬性,再看下下一張 圖:
將上面圖對照,發現用紫色框標識的 [36]R [39]R轉變成了[36] [39],從這裡發現從 有根 ---->無根 轉變了。這和GC中無根對象才會被回收是一個道理。
當托管堆對象2代滿了時會自動清 理0,1,2代的垃圾。有一個特殊情況,當1代中對象代+1後,轉變為2代,與原來2代的對象總共的內存超過了 容量,就有可能使應用程序中斷。(不過本人這裡也不太清楚,本人將所有的對象之空,設置為null)
最後一點:本來想用Timer來定時觸發對象的生成操作,代碼如下:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 private void btnAutoAdd_Click(object sender, EventArgs e) { timer = new System.Timers.Timer(3000); timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Tick); timer.AutoReset = true; timer.Enabled = true; btnAutoAdd.Enabled = false; btnAutoStop.Enabled = true; } public void timer_Tick(object sender, ElapsedEventArgs e) { NewOneObjectOperation(); } private void btnAutoStop_Click(object sender, EventArgs e) { timer.Stop(); btnAutoAdd.Enabled = true; btnAutoStop.Enabled = false; }
但是對於Panel的refresh()操作也是線程的,這樣的話將觸發異常:
本示例的目的是用一種視覺的效果來看我們.NET平台下的垃圾收集過程,本人水平有限,難免有N 多BUG以及不太清楚的地方,還請各位多多指教。
本GC初步模擬程序代碼下載地址:Jasen.GCShow.rar [GC初步模擬效果程序 ]
http://files.cnblogs.com/jasenkin/Jasen.GCShow.rar