冉彦中,张智刚,曹婧华,杨可扬
(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.