冉彥中,張智剛,曹婧華,楊可揚
(1.吉林大學和平校區計算機教研室,吉林長春 130062;2.吉林大學農學部圖書館,吉林長春 130062)
在進行Microsoft Visual Studio C#程序設計教學過程中,學生對垃圾回收器的垃圾回收工作機制[1-2]原理難于理解。為此,筆者用C#編程對數字型和字符型對象何時成為垃圾,第0代對象滿256k時啟動垃圾回收器進行垃圾回收以及完整的第0、1、2代對象回收工作的模擬演示3個實驗,便于學生理解其內容。
在Form 窗體上放Button和RichTextBox 兩個控件對象(圖1),Button1為單擊確定按鈕事件,在RichText-Box1中輸出如圖1 一行字,用代碼編程實現。
圖1 對象生成期實驗圖
在.net中對象是創建在托管堆之上的,以前使用的編程語言創建對象是在非托管堆中的,故編程人員往往忘掉了釋放無用的內存或再試圖訪問已經被釋放的內存,對于非托管編程來說因為這兩類應用程序發生的時間和次序都難于預測,帶來的危害超過了其它大多數的bug,其應用程序的執行結果變得不可預測,在.net中托管堆編程中[3-5],回收內存的工作由垃圾回收機制解決。對于垃圾回收機制,首先要了解對象的生成期,也就是對象什么時候變成垃圾,如圖2。
圖2 對象生成期代碼斷點調試圖
在int k=10 處設斷點演示對象的生存期(圖2),在程序開始運行的時候,RichTextBox類,button類等創建、運行程序,在托管堆有Mainform類以及成員int tempint,string temps 以及兩個控件RichTextBox1,Button1對象。單擊按鈕,創建一個整數類型k,并把它初值設為10,但它是在堆棧中創建的,被彈出堆棧就沒有用了,按F11 單步執行,創建一個字符串s,它是一個引用類型變量,它在托管堆中創建,整數類型是一個值類型對象,它是在堆棧中創建,按F11 創建一個i,但它是在堆棧中創建的,執行完循環以后,i 就自動消失了,因為再也不會使用它了,執行完button 后引用類型s 變為垃圾,因為它不會再被使用到,因此變為托管堆中的垃圾,它要等待垃圾回收器在某個特定時間里進行回收。當關閉窗體后,以上一些類都會成為垃圾,等待被釋放。
實驗設計從控制臺取字符串,每取一字符串就創建構造函數[6-9]public A()
圖3 構造函數析構函數運行圖
如圖3,類A 對象創建完就成為垃圾,再通過一循環創建一字符數組,創建長度為1000 的字節數組,也就是這個字節數組會占用1k 的空間,第0代對象為250k,每從屏幕取字符,讓它占用50k 空間,回車5 次占用250 空間,此時進行垃圾回收,釋放前面5個對象,從而證實了第0代對象為256k 的內存空間。
創建一個新對象時,若對象的類型定義了Finalize()方法,那么指向該對象的指針將被放到終結鏈表中。終結鏈表上的每一個條目都引用著一個對象,指示Garbage Collector[10-12]在回收這些對象之前調用它們的Finalize()方法,其過程如下。
(1)自創建一個類,并以該對象來進行操作,然后對該類對象集合實現遍歷以及索引,其次是該對象集合的管理類,負責所有對象的ItemCollection,以及0,1,2代對象集合,以及終結鏈表,終結可達隊列。
(2)初始化一個屬性值為隨機值的DataObject 對象。
(3)判斷托管堆0代內存是否充足,判斷過程:
(4)0,1,2代的比較判斷與操作
托管堆0代對象的主要操作。
圖4 垃圾回收驗機制證實驗圖
當一直添加對象時,執行情況如圖4所示,當托管堆0代對象內存容量256k 不足時,會觸發垃圾器收集;其中先清理可達隊列中的數據對象(含有Finalize()終結方法并且無根,一般情況為在第1 次收集時將終結鏈表中的指針移動至終結可達隊列中,這樣可達隊列中才有指針。第2 次收集就會將可達隊列中的指針清理),將終結鏈表的數據移動到可達隊列后,然后再移除終結鏈表包含的可達隊列的指針。
托管堆1代對象的操作。繼續創建新的對象,在托管堆1代中存在,一直增加,當托管堆0代對象內存不足,并且托管堆1代對象內存也不足時,將導致1代對象的代+1;其中也包括1代對象的清理工作。
托管堆2代對象的操作。托管堆2代對象內存滿了,清理托管堆2代無根對象,托管堆1代對象,對象代+1;托管堆對象全部清理,當托管堆對象2代滿了時會自動清理0,1,2代的垃圾。
首次初始化添加對象為0代對象,當托管堆中0代放滿時,若有新對象加入則垃圾回收器把0代中沒有使用的對象回收,把0代的對象挪到1代中,再把新對象添加托管堆的0代中。若0代放滿而1代沒放滿,回收器只把0代中不再使用的對象回收,而1代中的不變,之后再把0代挪入1代,把新對象添加到0代,當第1代也放滿時,若再有新對象加入,垃圾回收器把0代和1代中沒有使用的對象回收,再把0代的對象挪到1代中,1代的對象挪到2代中,之后再把新對象添加托管堆的0代中。由此看出,一般第0代對象回收頻率是最高的,效率也是最高的。
[1]Microsoft Visual C#2008 help[Z].2008.
[2]美國微軟公司Visual C#2005 軟件的幫助信息[Z].2005.
[3]葉傳華.基于C#.NET 的通用數據訪問接口的實現與應用[J].數字技術與應用,2010(10).
[4]郭勝等.C#.NET 程序設計[M].北京:清華大學出版社,2002.
[5][美]微軟公司.Visual C#.NET 語言參考手冊[M].熊盛新,許志慶,李欽,譯.北京:清華大學出版社,2002.
[6]韓彩云,白尚旺,黨偉超,等.基于C#.NET 的系統通用菜單的設計與實現[J].艦船電子工程,2008(11).
[7]賈曉飛.基于.NET 教師檔案管理系統的設計與實現[D].長春:吉林大學,2010.
[8]陳巍.基于.NET 大學生住宿管理系統的設計與實現[D].長春:吉林大學,2011.
[9]李文超.基于.NET 框架的政府文檔信息管理系統的設計與實現[D].長春:吉林大學,2011.
[10]吳茂昌.基于.NET 平臺分層架構的研究[D].武漢:武漢科技大學,2010.
[11]劉甫迎,劉光會,王蓉.C#程序設計教程[M].2 版.北京:電子工業出版社,2008.
[12]施燕妹,陳培,陳發吉.C#語言程序設計教程[M].北京:中國水利水電出版社,2004.