人生亦编程

--Programmable Life
posts - 86, comments - 43, trackbacks - 0, articles - 7

关于代码效率

Posted on 2006-08-31 08:42 人生亦编程 阅读(539) 评论(0)  编辑 收藏 引用 所属分类: 程序
有个叫profiler的工具,是用来检测代码效率的,现转用法如下,用得着的时候再去学它。

NetBeans IDE 4.1/5.0 Profiler 教程

本文旨在介绍配合使用 NetBeans Profiler v5.0 或 Profiler Milestone 8 的内容。如果您使用的是 Milestone 5,请使用此版本。如果您使用的是 Milestone 6,请使用此版本

预计持续时间:50 分钟

NetBeans Profiler 是一个功能强大的工具,它提供了有关应用程序运行时行为的重要信息。NetBeans Profiler 产生的开销相对来说较小,它可以跟踪线程状态、CPU 性能和内存使用情况。本教程将向您介绍如何使用 NetBeans Profiler 来完成以下操作:

  • 监视应用程序的运行时行为,其中包括:
    • 堆内存大小
    • 垃圾回收统计信息
    • 线程计数
    • 线程状态:运行、休眠、等待、受阻
  • 确定应用程序方法使用的 CPU 时间
  • 监视应用程序创建对象的过程

先决条件

本教程假定您已经具有了一些基本的 Java 编程和 NetBeans IDE 使用经验。

教程所需的软件:

  • 下载并安装 Java 2 Platform Standard Edition v5.0, Update 4(或更高版本)。请注意,NetBeans IDE 需要完整的 JDK,而不仅仅是 JRE。请记录此安装目录。
  • 下载并安装 NetBeans IDE v4.1 或 v5.0。请记录此安装目录。当系统提示您选择 JDK 时,请指定 v5.0, Update 4(或更高版本的)JDK。
  • 下载并安装用于 NetBeans IDE v5.0 的 NetBeans Profiler v5.0。如果使用的是 NetBeans IDE 4.1,请下载 Milestone 8
    Profiler 安装程序会要求您修改 NetBeans IDE 安装;请指定用于 NetBeans IDE 的安装目录。
    注意:Profiler v5.0 只能与 NetBeans IDE v5.0 一起使用;而 Profiler Milestone 8 只能与 NetBeans IDE v4.1 一起使用。
  • 下载教程 zip 文件,其中包括练习中使用的项目。

本文使用的表示法

  • <NETBEANS_HOME> - NetBeans IDE 安装目录
  • <USER_HOME> - 用户的 home 目录
  • <tutorial_root> - 解压缩教程 zip 文件所在的目录
    • 本文使用 <tutorial_root> 来表示解压缩此教程的 zip 文件所在的目录。此教程的 zip 文件的名称为 profilertutorial.zip
    • 将教程 zip 文件解压缩到 <tutorial_root> 目录时,系统会创建一个名为 netbeansprofiler 的子目录。例如,在 Windows 操作系统中,如果已将教程 zip 文件解压缩到驱动器 E:\ 的根目录中,则系统会创建 E:\netbeansprofiler 子目录。在 Solaris/Linux 操作系统中,如果已将教程 zip 文件解压缩到 /home/username 目录中,则系统会创建 /home/username/netbeansprofiler 子目录。

教程练习

参考资源:



练习 0:安装并配置教程环境

在开始学习教程之前,请先检查以下内容:

  1. 启动 NetBeans IDE,并确认 IDE 已打开。通过选择“性能分析”>“帮助”>“关于性能分析器...”,验证安是否装了 NetBeans Profiler。
    • 在 Windows 操作系统中,双击 NetBeans IDE 图标
    • 在 Solaris/Linux 操作系统中,打开终端窗口,然后键入 <NETBEANS_HOME>/bin/netbeans
  2. 仅限 NetBeans IDE v4.1:安装所需的修补程序。
    1. 从“工具”菜单中选择“更新中心”,以访问 NetBeans 更新中心。
    2. 确保选中了“NetBeans 修补程序更新中心”,然后单击“下一步”按钮。建立连接后,将显示一个更新列表。
    3. 在“NetBeans 修补程序更新中心”条目下,选择“NetBeans Profiler(J2EE 项目 - Tomcat5 支持)”。该操作还将自动选择 "Tomcat 5 Server" 条目。
    4. 单击“下一步”按钮,并按照说明进行操作以便 IDE 可以安装这些修补程序。




练习 1:监视 Swing 应用程序中的线程状态(15 分钟)

本练习的学习目的:

在本练习中,您将学习如何使用 NetBeans Profiler 监视 J2SE 应用程序中的线程状态。这样,您就可以诊断样例应用程序中的性能问题。

背景信息:

Swing 库为 J2SE 应用程序提供了图形用户界面组件。Swing 库会使用到多个线程,利用 NetBeans Profiler 这一功能强大的工具,您可以分析出每个线程所花费的处理时间,从而利用这些分析信息来解决性能问题。

执行步骤:

  1. 选择“文件”>“打开项目”。找到 <tutorial_root>/netbeansprofiler/exercises 文件夹。选择 exercise1 文件夹,然后单击“打开项目文件夹”按钮。
  2. 在“项目”窗口中,右键单击 exercise1 条目并选择“清理并生成项目”。然后,再次右键单击 exercise1 条目并选择“运行项目”。此时,将显示该程序的窗口,如下所示。

    查看应用程序的窗口

  3. 将 "Seconds Before Notification" 设置为 30。
  4. 单击 "Start!" 按钮。请注意,该程序无法正确地刷新屏幕。
  5. 单击 "Exit" 按钮。请注意,该程序根本不会作出响应。
  6. 将另一个窗口放在该窗口的某一部分上,以遮挡该窗口的视图。当移开另一个窗口时,您会注意到样例应用程序没有相应地刷新其窗口。如下面的示例所示。


  7. 30 秒后,最终将显示一个消息框,如下所示。单击消息框上的 "OK" 按钮。


  8. 应用程序窗口将再次开始响应。单击 "Exit" 按钮将其关闭。
  9. 选择“性能分析”>“分析主项目”。
  10. 如果出现一个对话框,询问您是否允许修改项目生成脚本,请单击“确定”。如下面的示例所示。


  11. 此时会显示“选择性能分析任务”对话框。单击占据较大区域的“监视应用程序”按钮。
  12. 应选中“启用线程监视”复选框。
  13. 单击“运行”按钮。如果显示一个对话框,指示您必须先收集 Profiler 的校准信息,请单击“确定”按钮。如下面的示例所示。

    选择“性能分析”>“高级命令”>“运行性能分析器校准”。完成校准后,Profiler 会显示一个对话框,请单击“确定”按钮。如下面的示例所示。

    要进行性能分析,请返回至上面的第 9 步。

  14. 完成上述步骤后,将启动该应用程序,并且 IDE 会显示“性能分析器”控制面板,如下所示。


  15. 性能分析器将在其主窗口中显示线程状态,如下面的示例所示。


    它使用颜色编码来显示线程状态。

    • 绿色:线程正在运行或准备运行。
    • 紫色:线程正在调用 Thread.sleep(),处于休眠状态。
    • 黄色:线程正在调用 Object.wait(),处于等待状态。
    • 红色:线程在尝试访问同步的块或方法时受阻。

  16. 找到样例 Swing 应用程序的窗口(NetBeans IDE 窗口可能在其上面)。单击样例应用程序中的 "Start!" 按钮,同时监视标记为 AWT-EventQueue-0 的线程所发生的变化。它将变为绿色并持续这种状态整整 30 秒钟,如以下样例所示。

    此图形显示了应用程序没有响应的原因。标记为 AWT-EventQueue-0 的线程是 Swing 用来处理窗口事件的事件分发线程 (Event Dispatch Thread, EDT)。在正常运行的 Swing 应用程序中,EDT 的大部分时间处于等待状态而运行的时间却很少,因为它只有在分发事件时才会运行很短的一段时间。但是,如果应用程序中的事件处理程序未立即返回,则程序将停止响应,就像在此示例中一样。

  17. 30 秒后,屏幕将显示应用程序的消息框。找到该消息框(NetBeans IDE 窗口可能在其上面)并单击 "OK" 按钮。然后,单击样例应用程序的 "Exit" 按钮,将其关闭。
  18. “项目”窗口与“性能分析器”控制面板共享同一个空间;单击“项目”标签将会显示“项目”窗口。
  19. 在“项目”窗口中,双击 exercise1 条目以将其展开,然后展开“源包”和 profilingthreads 条目。右键单击 DisplayForm.java 条目并选择“打开”。
  20. 在包含 DisplayForm.java 的编辑器窗口中,取消第 132 行至第 157 行的代码块注释。提示:如果未将编辑器配置为显示行号,请选择“视图”>“显示行号”。
  21. 在包含 DisplayForm.java 的编辑器窗口中,取消(或注释掉)第 122 行到第 128 行的代码块注释。下图突出显示了取消注释的代码块。


  22. 选择“文件”>“保存”。您刚才删除了未正确使用 EDT 的代码,并利用了更稳定可靠的解决方案来替代它。现在,应用程序可以及时地作出响应。
  23. 选择“生成”>“生成主项目”(或按 F11 键)。
  24. 选择“性能分析”>“分析主项目”。
  25. 将显示“选择性能分析任务”对话框。单击占据较大区域的“监视应用程序”按钮。
  26. 确保选中了“启用线程监视”。
  27. 单击“运行”按钮。
  28. 在显示样例程序时,单击 "Start!" 按钮。请注意,此时的性能分析线程图已经发生了变化,如以下示例所示。

    EDT 为黄色,应用程序创建的名为 "Our SwingWorker #1" 的线程为绿色。由于 EDT 不是用来执行耗时任务的,因此,按钮和其他程序控件仍保持响应的状态。

  29. 在样例程序中单击 "Exit"。
  30. 右键单击 Profiler 图形中的 AWT-EventQueue-0 线程,然后选择“线程详细信息”。Profiler 会显示一个饼图,表明了每种状态所花的时间;如以下示例所示。

    该图形可以帮助您判断程序在每个线程中所花费的时间是否恰当。上述示例是代码修复后的示例,因此,它显示了 EDT 在大部分时间中都处于等待状态的情形,这正好是该线程应具有的行为。

  31. 在“项目”窗口中,右键单击 exercise1 条目,然后选择“关闭项目”。

小结:

在本练习中,您学习了如何使用 Profiler 来启动应用程序,以及如何解释 Profiler 的线程信息图形,以此来跟踪 Swing 应用程序中的性能问题。

返回页首



练习 2:确定某个方法使用的 CPU 时间(15 分钟)

本练习的学习目的:

在本练习中,您将学习如何使用 Profiler 来确定某个应用程序的方法所花费的时间。

背景信息:

CPU 的性能问题通常与应用程序的特定功能有关。例如,在报告系统中,某个报告的运行速度可能比其他报告慢。只分析应用程序中出现性能问题的部分,可以大大减少性能分析器产生的开销。在本练习中,您将使用 NetBeans Profiler 来检查 Web 应用程序中 CPU 的使用情况。在练习 3 中,我们仍然使用该样例 Web 应用程序来说明如何通过 Profiler 查找内存泄漏。

执行步骤:

  1. 选择“文件”>“打开项目”。找到 <tutorial_root>/netbeansprofiler/exercises 文件夹。选择 exercise2 文件夹,并确保选中“作为主项目打开”。单击“打开项目文件夹”按钮。
  2. 在“项目”窗口中,单击 exercise2 条目,然后从菜单中选择“生成”>“清理并生成主项目”。
  3. 从菜单中选择“性能分析”>“分析主项目”。如果出现一个对话框,询问您是否允许修改项目生成脚本,请单击“确定”。
  4. 将显示“选择性能分析任务”对话框。
  5. 单击“分析性能”按钮。
  6. 选择“部分应用程序”单选按钮。然后,单击位于“部分应用程序”单选按钮旁边的“选择”按钮。此时将显示“指定根方法”窗口。

    注意:下面的三个步骤仅适用于 Milestone 8。

  7. 在“指定根方法”窗口中,单击“从项目添加...”按钮以添加要分析的根方法。将会显示“选择方法”窗口。
  8. 在“选择方法”窗口中,单击“选择”按钮。Profiler 将显示“选择类”窗口。
  9. 在“类名”文本字段中输入 Per。等待 IDE 为您显示适用的类列表。选择 Performance 类。单击“打开”按钮以打开其方法列表。

    注意:下面的两个步骤只适用于 Profiler v5.0。

  10. 在“指定根方法”窗口中,单击“从项目添加...”按钮以添加要分析的根方法。将会显示“选择方法”窗口。
  11. 在“类名”文本字段中,键入 demo.Performance,然后按 Enter 键。

    查看“选择方法”对话框

  12. 在根方法列表中单击 processRequest 方法以将其选中,然后单击“确定”按钮。您刚才已将 demo.Performance.processRequest 选中为性能分析的根方法。这意味着,性能分析器将分析 demo.Performance.processRequest 方法和它调用的所有方法,以及这些方法又调用的所有方法,依此类推。性能分析器将从 demo.Performance.processRequest 开始,通过分析方法调用图形来确定哪些方法需要分析。它将只分析那些需要分析的方法,应用程序的其余部分将在没有任何性能分析开销的情况下继续以最快的速度运行。
  13. 在“指定根方法”窗口中单击“确定”按钮。
  14. 在“分析性能”窗口中,从过滤器列表中选择“快速过滤器”,然后单击位于“快速过滤器”条目旁边的 "..." 按钮以指定进行分析的方法。将显示“设置快速过滤器”窗口。验证是否已选中了“排除”单选按钮。然后,在“过滤器值”文本字段中输入 org.apache 并单击“确定”按钮。这样,性能分析器将不分析所有 org.apache 包(及其子包)中的方法,即使这些方法被选定的根方法调用也是如此。这会减少性能分析的开销,并过滤掉无关的信息。

    “设置快速过滤器”对话框

  15. 在“选择性能分析任务”窗口中,单击“运行”按钮。如果出现一个对话框,要求您提供校准信息,请单击“确定”按钮;在性能分析器显示的对话框说明校准已完成后,请单击该对话框的“确定”按钮。
  16. IDE 将启动 Tomcat,并在 Web 浏览器窗口中显示 Web 应用程序的 index.jsp 页。同时,将在后台运行性能分析器。注意:缺省情况下,将在成为焦点的浏览器窗口中显示 index.jsp 页;您可能需要打开另一个浏览器窗口,以便在运行 Web 应用程序时可以阅读这些说明。
  17. 因为已选择了 demo.Performance.processRequest 作为根方法,所以,您需要使用导致该根方法运行的 Web 应用程序部分:在 Web 浏览器中,单击“性能问题”以转至“性能”演示页。在“性能”演示页上的输入文本字段中输入 123456。请不要选中“已优化”选项。单击“提交查询”按钮,计算小于或等于 123456 的最大素数。
  18. 需要几秒钟的时间才能出现响应,这是因为计算最大素数的算法存在一些性能问题,同时,在监视 processRequest 方法的性能时,性能分析器也会产生一些开销。
  19. 当浏览器中显示 "123449" 结果后,请单击 “获取结果”按钮 按钮,或者选择“性能分析”>“生成收集结果的快照”。单击位于 CPU 使用情况快照底部的“组合”标签。性能分析器将显示最新的性能结果,如下所示。

    查看性能结果。

    顶部窗口显示了从根方法开始的完整方法调用图形。底部窗口是重点描述的部分;它显示了应用程序中的热点,即执行时间最长的那些方法。

  20. 要查看并解释这些结果,请注意 processRequest 方法总共运行的时间为 4308 毫秒 (ms)。然而您会发现,运行 processRequest 方法本身的指令只花费了很少的时间,processRequest 的“自用时间”仅为 10.1 毫秒。绝大部分时间花在了 processRequest 调用的其他方法上。底部窗口中显示的热点是按“自用时间”进行排序的。通过查看该列表,您可以看到 calculate 方法占用了 97.8% 的执行时间。考虑到为 calculate 方法分配的工作量,这就不足为奇了。
  21. 为了帮助您确定如何优化应用程序,NetBeans Profiler 可以识别代码中无法预料的瓶颈,或者识别妨碍应用程序随意缩放的瓶颈。单击热点列表中的 calculate 条目以了解时间究竟花在什么地方,这会更新调用图形窗口以显示 calculate。因为 calculate 方法不调用任何其他方法,所以请右键单击 calculate 条目,然后选择“转至源代码”以便检查源代码。您会发现它使用了效率很低的算法,所以应该重新设计这种算法。
  22. 选择“性能分析”>“重置收集的结果”以清除性能分析器的缓冲区。要将 calculate 的运行时与优化的算法 calculate2 作比较,请在 Web 浏览器中选中“已优化”选项,然后单击“提交查询”。等待显示结果,然后重新选择“性能分析”>“生成收集结果的快照”。请注意,processRequest 方法只运行了 107 毫秒,而 calculate2 方法所花的时间还不到执行时间的 10%!

    查看性能结果。


  23. 注意:我们将继续在本练习的基础上进行下面的练习,因此,请不要关闭任何窗口。

小结:

在本练习中,您学习了如何使用性能分析器执行方法性能分析。

返回页首



练习 3:分析对象创建过程以查找内存泄漏(20 分钟)

本练习的学习目的:

作为练习 2 的后续练习,我们将在本练习中学习如何解释某些性能分析器的图形以监视应用程序创建对象的过程。下面将显示一个内存泄漏示例。

执行步骤:

  1. 本练习是在练习 2 的基础上进行的,因此,请务必遵循练习 2 中的步骤。
  2. 从菜单中选择“性能分析”>“分析主项目”。如果出现一个对话框,询问您是否要停止当前的性能分析器进程以启动新进程(如下所示),请单击“是”按钮继续执行。

    “停止并重新启动性能分析器”对话框

    将显示“选择性能分析任务”对话框。

  3. 在“选择性能分析任务”对话框中,单击占据较大区域的“监视应用程序”按钮。
  4. 单击“运行”按钮。IDE 将会在左侧显示“性能分析器”控制台。单击 查看“遥测概览”按钮 按钮,或者选择“性能分析”>“视图”>“遥测概览”。NetBeans Profiler 将在底部的输出窗口中显示三个图形,如下所示。

    查看监视器图形

    在左侧的图形中,红色的阴影部分表示分配的 JVM 堆大小。紫色的覆盖部分表示实际使用的堆空间大小。在上面的示例中,上次更新所分配的堆大小已超过了 20 MB。其中,实际用来保存 Java 对象的堆大小略大于 10 MB。

    右侧图形显示了 JVM 中的活动线程数。

    中间的图形显示了两种重要的堆统计信息。

    • 蓝线是 JVM 执行垃圾回收的时间占执行时间的百分比,它是以图形右侧上的 Y 轴为参照绘制的。JVM 执行垃圾回收所花的时间不能用来运行应用程序。因此,如果蓝线占据较大的百分比,则需要考虑调整 JVM,方法是:配置更大的堆大小(请参阅 -Xmx 参数文档),或者转换到不同的垃圾回收算法中。
    • 红线表示存活的生成数,它是以图形左侧的 Y 轴为参照进行绘制的。存活生成数是指 JVM 堆上所有 Java 对象不同生存期的数量,其中“生存期”被定义为对象存活时的垃圾回收次数。如果存活生成数的值较小,则表明堆上的大部分对象的存活时间基本相同。但是,如果存活生成数的值随着时间的变化而增长到一个很高的比率,则表明应用程序正在分配新的对象,同时保持对已分配的多个旧对象的引用。如果实际上不再需要这些旧对象,则应用程序正在浪费(或“泄漏”)内存。

  5. 性能分析器可以对 CPU 性能或内存使用情况进行详细的分析,但不能同时进行这两种分析。要了解有关在 JVM 堆上分配对象以及执行对象的垃圾回收的详细信息,请修改性能分析器的设置。单击 查看“修改性能分析”按钮 按钮,或者选择“性能分析”>“修改性能分析”。
  6. 单击“分析内存使用情况”按钮。
  7. 选择“记录对象创建和垃圾回收”单选按钮。
  8. 选中“记录分配的栈跟踪”复选框。
  9. 单击“确定”按钮。
  10. 现在,您已经选择了分析内存使用情况,您需要运行应用程序以确定它是否有效地使用了内存。单击“内存泄漏”以转至 MemoryLeak 演示页。
  11. 在 MemoryLeak 演示页上,单击“开始泄漏”。
  12. 请注意存活生成数图形中的峰值,如以下示例所示。这表明可能出现了内存泄漏。

    存活生成数图形所示的峰值。

  13. 选择“性能分析”>“视图”>“实时结果”。Profiler 将显示 JVM 堆中分配的对象的动态视图。缺省情况下,它将按每个类的所有实例使用的字节数进行排序。由于怀疑可能出现了内存泄漏,因此,请单击“生成数”列,按每个类的不同对象生存期数量对显示结果进行排序。下面显示了得到的显示结果示例。

    先前的生成数图形。
    这些列提供了对象分配和内存使用情况信息。

    • “分配的对象”是性能分析器正在监视的对象数。在本示例中,共监视了 38 个 float[] 实例。缺省情况下,该数字约为应用程序实际分配的对象数的 10%。通过只监视已创建对象的一部分,性能分析器可以显著地减少它在 JVM 中的开销,这样,应用程序就几乎可以按最快的速度运行了。
    • “活动对象”是仍在 JVM 堆中并因此占用内存的已分配对象数。
    • 两个“活动字节”列显示了活动对象所占用的堆内存量。一个列显示图形,另一个列显示文本。
    • “平均生存期”值是使用活动对象计算的。每个对象的生存期是它存活时的垃圾回收次数。生存期总和除以活动对象数得到的结果就是平均生存期。
    • “生成数”值是使用活动对象计算的。与平均生存期相同,对象生存期是它存活时的垃圾回收次数。“生成数”值是活动对象的不同生存期数量。

  14. 随着程序继续运行,性能分析器将更新显示结果。请留意 float[]double[] 条目。请注意其生成数值是如何持续增加的。结果是,float[]double[] 在列表中持续上移。最终,它们会显示在列表的顶部,紧靠 java.util.HashMap$Entry(其生代数值也在不断增加)下面。随着应用程序继续运行,java.util.HashMap$Entryfloat[]double[] 生成数值持续增加,但任何其他类没有增加。如下面的示例所示。

    后期的生成数图形。

  15. 要了解导致生成数值持续增加的原因,请选择“性能分析”>“生成收集结果的快照”。按生成数对显示结果进行排序。右键单击 double[] 条目,然后选择“显示分配栈跟踪”。性能分析器将显示所有分配了一个或多个 double[] 对象的方法。如下面的示例所示。

    double[] 栈跟踪。
    请注意,在分配了 double[] 对象的方法中,只有一个方法创建了具有较大生成数值的 double[] 对象。该方法是 run(),它位于具有相应名称 demo.memoryleak.LeakThread 的类中。

  16. 右键单击 run() 方法条目,然后选择“转至源代码...”。性能分析器将显示源代码,如下所示。引起泄漏的代码
    请注意,正在分配 double[]float[] 对象,并随后将其放在 HashMap 中。但一直并未删除它们,这意味着,HashMap 保留的引用将会妨碍 JVM 对这些对象进行垃圾回收。这是非常典型的 Java 内存泄漏:将对象放在 Map 中,然后就忘记处理它们了。由于本示例中使用的 Map 就是 HashMap,因此,关联的 java.util.HashMap$Entry 对象也出现了泄漏。

  17. 选择“性能分析”>“停止”来结束性能分析会话,或者单击 “停止”按钮 按钮。
  18. 在“项目”窗口中,选择 exercise2 条目,然后从菜单中选择“文件”>“关闭项目”。

小结:

在本练习中,您学习了如何使用性能分析器来监视应用程序创建对象的过程。您还看到了性能分析器在应用程序出现内存泄漏时所提供的各种类型的分析指数。


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理