在Heap Usage Chart中,蓝色部分表明已分配的堆空间大小。在启动这个Java程序并达到稳定状态以后,我强制垃圾收集器运行,在图中的表现就是绿线(这条线表明插入了一个检查点)左侧的蓝线的骤降。随后,我添加了四个窗体,然后又将它们删除,并再次调用了垃圾收集器。当程序返回仅有一个可视窗体的初始状态时,检查点之后的蓝色区域高于检查点之前的蓝色区域这一情况表明可能存在内存漏洞。我通过查看Instance Summary证实确实有一个漏洞,因为Instance Summary表明FormFrame类(它是窗体的主用户界面类)的计数在检查点之后增加4。
查找原因
为了将测试人员报告的问题剔出,我采取的第一个步骤是找出几个简单的、可重复的测试案例。就本例而言,我发现只须添加一个窗体,将它删除,然后强制执行垃圾收集,结果就会导致与被删除窗体相关联的许多类实例仍然处于活动状态。这个问题在JProbe的Instance Summary视图中很明显,这个视图统计每个Java类在堆中的实例数。
为了查明使垃圾收集器无法正常完成其工作的那些引用,我使用JProbe的Reference Graph(如图 3 所示)来确定哪些类仍然引用着目前未被删除的FormFrame类。在调试这个问题时该过程是最复杂的过程之一,因为我发现许多不同的对象仍然引用着这个无用的对象。用来查明究竟是哪个引用者真正造成这个问题的试错过程相当耗时。
在本例中,一个根类(左上角用红色标明的那个类)是问题的发源地。右侧用蓝色突出显示的类处在从最初的 FormFrame 类跟踪而来的路径上。