51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 3371|回复: 0
打印 上一主题 下一主题

[原创] 提高Java开发质量之"内存泄露"

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2006-4-28 18:20:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
原文地址:http://www.innovatedigital.com/htm_speek/speak14_index.shtml

当前,J2EE的开发质量的问题已经越来越突出,如果你的即将上线或已经上线的应用经常不稳定,特别在负载大时尤为明显,你经常忙于在最后时刻救火,那么应该考虑在开发的早期尽量进行性能方面的测试。本文将探讨在J2EE开发中,常见的解决办法,这里以Quest JProbe Suite 工具为例,说明在实际开发中应如何提高开发质量。

  "内存泄露"是目前Java应用中最为常见的问题之一,单元测试的不完善直接导致生产系统的不稳定。单元测试是解决"内存泄露"问题的比较适当的环节。这样可以避免在生产系统中进行冗长烦琐的搜集数据和分析工作。

  "内存泄露"简单地说,开发人员主观认为已经不再使用,而实际上在 JVM中仍然被使用的那些对象。往往是由于开发人员的疏忽、架构设计的问题或所使用第三方组件的瑕疵等造成的。

  这里我们使用Quest JProbe 来分析一个简单的用例。

  如下面三图所示,点击Add按钮十次,增加0-9个 Button(如第二图),然后点击 Remove按钮十次,如第三图。

  我们使用JProbe 记录该过程中内存中对象的变化情况,如下图:

    我们现在将寻找堆中的游离对象-即"内存泄露"对象。基于前面的假设,你希望Jbutton类的变化数量应该为0即堆中应该没有Jbutton实例了,因为在上面的试验中我们最后移走了所有创建的按钮。实际上,Jbutton对象没有被移走并且可能继续游离在堆中。

  1. 首先在Instance Summary中查看每个类的Count Change,如果该数据不为0则表示用例运行完后,JVM堆中该类的实例数发生了变化。因为我们要识别游离对象,就要查看用例运行完后,JVM堆中的实例数是否发生了变化,也就是在执行时引用的对象到最后是否被释放。如果用例运行完后,堆中的实例数发生了变化,则说明可能存在游离对象。如果Count Change列没有显示出来,右击Class List,选择Show/Hide Columns,选中Count Change点击OK。

  2. 在Filter Classes域中输入*.Jbutton可以快速定位出Jbutton类,因为在上面的试验中我们主要创建了Jbutton对象,所以我们在这里主要观察Jbutton类。

  3. 我们注意到Jbutton类的Count Change列显示为+10,这表示从开始运行用例到结束用例运行这段时间内,堆中增加了10个Jbutton对象。尽管我们主观认为在试验最后已经销毁了这十个对象,但实际上它极有可能还存在于堆中。

  到这里我们已经识别出堆中很有可能存在游离的对象,下一步我们将进一步定位到类和代码行。

    这部分我们将找到究竟是哪些存活对象还持有Jbutton游离实例的引用。打开Class View窗口查看snapshot中的数据,通过Instance Detail View可以更深入地看到Jbutton的细节信息,最后打开Source窗口我们将看到原来是Jbutton[ ]数组仍然持有游离对象Jbutton。

  1. 选中要分析的snapshot,点击Class View。打开的窗口显示了堆中的类。

  2. 选中Jbutton类并点击Instance Detail View。这样可以查看到该类更详细的信息,包括该类的实例化信息,一共实例化了多少个对象,每个对象都是由谁创建的,对象的调用树和被调用树等。

  3. 点击工具条上面的View Only Instance Allocated After Checkpoint。这将移走那些在前面设置断点后内存中分配的对象,因为我们主要关心的是用例是否存在游离对象,所以不必查看运行用例前内存中的信息,现在你应该只看到十个实例。

  4. 选中任意一个Jbutton实例并点击Memory Leak Doctor,打开Memory Leak Doctor窗口。里面分别用不同颜色表示了对象的创建是在记录过程前和记录过程中等两种情况。游离对象通常在用例运行时即开始记录后增加的,但可能跟记录前创建的对象有点联系。对于这些游离对象,只有一个可能原因。以前存在的LeakExample实例引用Jbutton[ ]了,而Jbutton[ ]则引用了Jbutton实例。

  5. 我们模拟在程序中释放某个引用后,看该引用所关联的对象是否就可以被垃圾回收,如果可以垃圾回收说明可能存在游离对象。在这里我们看到Jbutton[ ]还持有Jbutton的引用,我们假定Jbutton是游离对象,选中Jbutton[ ]和Jbutton之间的引用,右键点击并且选择Remove Reference。这时系统将提示如果你在用例的程序中释放了这个引用,这个实例将被垃圾回收。这只是唯一一个引起游离对象的可能原因。

  6. 关闭Memory Leak Doctor窗口。接下来查看分配该实例的方法,并深入该分配方法的代码,查看该方法最后是否释放了该实例。

  7. 在Instance Detail窗口中,选中Jbutton实例,看Allocated At区域。我们将看到该实例是由方法LeakExample.addButtonToPanel( )分配的。

  8. 查看该分配方法的源代码,右键点击LeakExample.addButtonToPanel行并选中Allocated At Source。

  9. 找到用例的源代码,弹出的窗口显示选中的addButtonToPanel( )方法并定位到了分配Jbutton实例的代码行。至此,你看到了按钮是在什么地方被添加到buttons[ ]数组的了。

  10. 在Source窗口中找到removeButtonFromPanel( )方法。我们将注意到执行从button[ ]数组移走按钮的代码行嵌在if语句中,实际上,这个条件永远不能满足,它自始至终没有被执行到。这就造成了Jbutton实例一直存在于内存当中,变成了游离对象。(稍后我们可以修改其if的条件或满足该条件,让用例可以执行到里面移走Jbutton实例的代码,这时将看到内存明显得到改进并且内存中不再存在Jbutton实例)

  11. 到这里,我们已经证实button[ ]数组从用例开始运行到结束一直持用Jbutton实例的引用,我们可以肯定内存中存在游离对象。

    在运行改进后的用例前,你也可以使用JProbe计算这些游离对象一共消耗多少内存。选中Jbutton实例并从Tree Type下拉列中选择Reference Tree,这时你将发现Jbutton实例在内存中还引用了很多其它实例。点击Calculate就可以计算出这些游离对象消耗的内存大小了。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-25 16:51 , Processed in 0.059064 second(s), 25 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表