本文旨在与 NetBeans Profiler Milestone 5 搭配使用。
NetBeans Profiler 是一个功能强大的工具,它提供了有关应用程序运行时行为的重要信息。NetBeans Profiler 跟踪 CPU 性能和内存使用情况,它产生的开销相对较小。本教程介绍了如何将 Profiler 与 NetBeans 配合使用以完成以下操作:
本教程的部分内容最初包含在 2004 年 6 月发表的有关 JFluid(NetBeans Profiler 使用的一种基本技术)的 Java Developer's Journal 文章 (http://sys-con.com/story/?storyid=45081&DE=1) 中。所有再版内容都是在得到 Java 开发者日记发布者的允许后使用的。
设置环境
安装 Profiler
NetBeans Profiler 处于测试阶段,将作为单独的下载提供。要使用本教程,您必须安装最新版本的 NetBeans Profiler。请务必按照 NetBeans Profiler 下载和安装说明进行操作。
配置 Tomcat
在分析 Web 应用程序时,需要使用 NetBeans Profiler 附带的 JDK 运行 Web 服务器。因此,要将 Profiler 与 NetBeans 捆绑的 Tomcat 服务器一起使用,必须对 Tomcat 的启动方式稍做修改。要执行此操作,最简便的方法是修改 Tomcat 的启动脚本。该脚本是 catalina.bat(在 Windows 上)或 catalina.sh(在 Unix 上)。这些脚本位于 Tomcat 的 bin 文件夹中;Windows 系统上的典型位置是:
C:\Program Files\netbeans-4.0\nb4.0\jakarta-tomcat-5.0.28\bin\catalina.batUnix 上的位置类似于:
/export/home/gs/tools/netbeans/40/nb4.0/jakarta-tomcat-5.0.28/bin/catalina.sh
- 保存该脚本的副本以作为备份。
- 在文件顶部的较大注释块的正下方,添加一行以将 JAVA_HOME 设置为包含 NetBeans Profiler 附带的 JDK 的文件夹。在 NetBeans 中,选择 "Profiler" > "Help"(帮助)> "About Profiler"(关于 Profiler)以显示该文件夹名称。Windows 上的示例值:
set JAVA_HOME=c:\Documents and Settings\gs\.netbeans\4.0\modules\profiler-ea-vm\
Unix 上的示例值:JAVA_HOME="/home/gs/.netbeans/4.0/modules/profiler-ea-vm/"
获取所需的源文件
本教程使用的 Web 应用程序基于在 NetBeans IDE 4.0 Web 应用程序快速入门指南中创建的 HelloWeb 项目。本教程所做的修改包括添加了一个 Servlet 以处理传入请求。它创建一个 Java Bean,然后将其分发给创建响应的 JSP。为了重点说明 NetBeans Profiler 功能,我们有意没有正确编写该 Servlet。
您可以从 ProfilingTutorial.zip 文件中获取所有应用程序文件。
- 在您的文件系统中,创建一个文件夹以存储解压缩的应用程序文件。从现在起,我们将该文件夹称为 $UNZIPHOME。
- 单击此处以下载 ProfilingTutorial.zip 文件。
- 使用可解压缩文件的应用程序将 ProfilingTutorial.zip 文件解压缩到 $UNZIPHOME 中。
$UNZIPHOME 现在包含 web 和 src 文件夹。web 文件夹包含两个 JSP 文件和部署描述符。src 文件夹包含 Servlet 和 Java Bean 类的源文件。
创建项目
通过提供的源文件创建 Web 项目
- 选择 "File"(文件)> "New Project"(新建项目 (Ctrl-Shift-N))。在 "Categories"(类别)中,选择 "Web"。在 "Projects"(项目)中,选择 "Web Project with Existing Sources"(基于现有源代码的 Web 项目)。单击 "Next"(下一步)。
- 在 "Location"(位置)中,单击 "Browse"(浏览)以选择 Web 应用程序的文档根目录。文档根目录是将 ProfilingTutorial.zip 文件解压缩到的 $UNZIPHOME 文件夹。
- 在 "Project Name"(项目名称)中,键入 ProfilingTutorial。单击 "Next"(下一步)。
- 单击 "Finish"(完成)。IDE 将创建 $UNZIPHOME/nbproject 项目文件夹和 $UNZIPHOME/build.xml 文件。ProfilingTutorial 项目将在 IDE 中打开。您可以在 "Projects"(项目)窗口中查看其逻辑结构,在 "Files"(文件)窗口中查看其文件结构。
- 只有在将 NetBeans 配置为使用 JDK 版本 5 作为缺省 Java 平台时,才需要执行最后一步。
- 当前的 Profiler 仅支持 JDK 1.4.2 版创建的 .class 文件,因此,如果未安装 JDK 1.4.2 版或未将其配置为 NetBeans Java 平台,则需要执行此操作。请注意,NetBeans Profiler 测试版安装一个基于 1.4.2 版的 JDK - 在配置 Tomcat 步骤中,应将其设置为 JAVA_HOME。有关详细信息,请参阅联机 Profiler 帮助。
- 在 "Projects"(项目)窗口中,右键单击 ProfilingTutorial 条目,然后选择 "Properties"(属性)。选择一个使用 1.4.2 版 JDK 的 Java 平台,然后单击 "OK"(确定)按钮。
- 在 Tomcat bin 文件夹中,创建 catalina.50.bat(在 Windows 上)或 catalina.50.sh(在 Unix 上)的备份副本。
- 对 catalina.50.bat(在 Windows 上)或 catalina.50.sh(在 Unix 上)进行与配置 Tomcat 步骤相同的编辑。
运行项目
- 在 "Projects"(项目)窗口中,右键单击 ProfilingTutorial 条目,然后选择 "Clean and Build Project"(清理并生成项目)。接下来,再次右键单击 ProfilingTutorial 条目,然后选择 "Run Project"(运行项目)。这将导致 IDE 生成项目,启动 Tomcat,然后在浏览器窗口中显示 index.jsp 页。
- 在 IDE 的 "Output"(输出)窗口中,单击 "Bundled Tomcat"(捆绑的 Tomcat)标签以验证 Tomcat 是否正确启动。滚动到 Tomcat 输出顶部以验证 Tomcat 是不是使用 NetBeans Profiler JVM 启动的。检查以 Using JAVA_HOME 开头的行;Java Home 目录应该是包含 NetBeans Profiler JVM 的文件夹。如果 Tomcat 未启动,或者 JAVA_HOME 不是包含 NetBeans Profiler JVM 的目录,请验证您是否按照配置 Tomcat 步骤中的说明进行操作。下面显示了 Windows 样例输出。
- 应打开了一个浏览器窗口并显示 http://localhost:8084/ProfilingTutorial/ 页。在名称字段中键入 NetBeans User,然后单击 "OK"(确定)按钮以验证 Web 应用程序是否正常工作。结果应类似于下图。
监视运行时行为
连接到运行的应用程序
现在,您已验证 Tomcat 使用的是 NetBeans Profiler 附带的 JVM,您可以将 IDE 的性能分析工具连接到该 JVM 并监测其运行时行为。
- 选择 "Profile"(性能分析)> "Attach Profiler"(连接 Profiler)。将显示 "Specify Attach Settings"(指定连接设置)对话框。
- 对于 "Attach To:"(连接至:),请选择 " J2EE Web/App Server"(J2EE Web/应用服务器)。
- 对于 "Running on:"(运行位置:),请单击 "Running on:"(此计算机)单选按钮。
- 对于 "Attach Settings"(连接设置),请单击 "Dynamic Attachment"(动态连接)单选按钮,然后单击 "Select Running VM"(选择运行的 VM)单选按钮。从 VM 列表中选择 Tomcat 条目(通常为 org.apache.catalina.startup.Bootstrap start)。
- 在 "Working Directory"(工作目录)中,键入 Tomcat bin 文件夹的全限定名称。例如,Windows 系统上的典型值为:
C:\Program Files\netbeans-4.0\nb4.0\jakarta-tomcat-5.0.28\bin
Unix 上的值类似于:/export/home/gs/tools/netbeans/40/nb4.0/jakarta-tomcat-5.0.28/bin
- 单击 "OK"(确定)按钮。将显示 "Attach and Profile"(连接并分析)对话框。
- 单击较大的 "Monitor Application"(监视应用程序)按钮。确保未选中 "Enable Threads Monitoring"(启用线程监视)选项。
- 单击 "Attach"(连接)按钮。
解释监视器图形
NetBeans Profiler 在输出窗口中显示三个图形,如下图所示。左侧的图形最容易理解。红色阴影表示分配的 JVM 堆大小,大约每秒更新一次。紫色的重叠部分表示实际使用的堆空间大小。在上面的示例中,最后更新时分配的堆大小大约为 14 MB。在这 14 MB 中,保存 Java 对象实际使用了 4 MB 多一点。
右侧的图形也很容易理解。它仅显示 JVM 中的活动线程数,大约每秒更新一次。
中间的图形最值得关注。它显示两种重要的堆统计信息。
- 蓝线是 JVM 进行垃圾回收所花的执行时间百分比,Y 轴位于绘制的图形右边缘。JVM 进行垃圾回收所花的时间不能用于运行应用程序。因此,如果蓝线所占的比例很大,您可能需要考虑调整 JVM,方法是配置较大的堆大小(请参阅 -Xmx 参数文档)或切换到不同的垃圾回收算法。
- 红线表示存活的年代数,Y 轴刻度位于绘制的图形左边缘。存活年代数是指 JVM 堆上的所有 Java 对象的不同生存期数,此处,“生存期”定义为对象经历的垃圾回收次数。如果“存活年代数”值较小,则表明堆上的大部分对象的存活时间基本相同。但是,如果“存活年代数”值随时间的推移以较高的比率增长,则表明应用程序正在分配新的对象,同时保持对分配的很多旧对象的引用。如果实际上不再需要这些旧对象,则应用程序正在浪费(或“泄漏”)内存。
![查看 'Telemetry Graphs'(遥测图形)按钮](https://netbeans.org/images_www/articles/win-with-netbeans/telemetryGraphsButton.png)
确定方法使用的 CPU 时间
切换到性能分析模式
Profiler 已连接到 Tomcat JVM,但仅监视概要统计信息。要了解应用程序中的一个或多个特定方法的性能详细信息,您需要修改 Profiler 设置。
- 单击
按钮,或者选择 "Profile"(性能分析)> "Modify Profiling"(修改性能分析)。
- 单击 "Analyze Performance"(分析性能)按钮。
- 选择 "Part of Application"(部分应用程序)单选按钮,然后单击 "Part of Application"(部分应用程序)单选按钮旁边的 "Select"(选择)按钮。
- 单击 "Add"(添加)按钮。
- 单击 "Select"(选择)按钮。Profiler 将显示 "Select Class"(选择类)树。
- 单击 ProfilingTutorial 条目的加号图标以展开该条目。
- 单击 "Source Packages"(源包)条目的加号图标以展开该条目。
- 展开 org.me.hello 条目。
- 展开 RequestHandler.java 条目。
- 单击 RequestHandler 条目,然后单击 "OK"(确定)按钮。
- 现在,将在 "Select Methods"(选择方法)对话框中显示 RequestHandler 类中的方法,如下图所示。单击 processRequest 方法,然后单击 "OK"(确定)按钮。
- 在 "Root Methods"(根方法)列表中单击以选择 processRequest 方法,然后单击 "OK"(确定)按钮。您刚刚选择了 processRequest 作为性能分析的根方法。这意味着,Profiler 将监视 processRequest 方法、它调用的所有方法以及这些方法调用的所有方法,依此类推。Profiler 分析方法调用图形(从 processRequest 开始)以确定需要分析哪些方法。它只分析这些方法,应用程序的其余部分将继续以最快的速度运行,而不会产生任何性能分析开销。
- 在 "Modify Profiling"(修改性能分析)对话框中,单击 "OK"(确定)按钮。
运行分析的方法
现在,您已选择了 processRequest 作为根方法,您需要使用导致运行该根方法的 Web 应用程序部分。这是很容易做到的,因为 processRequest 方法处理来自 index.jsp 页的所有请求。
- 在 Web 浏览器中,单击 "Back"(后退)按钮以返回到 http://localhost:8084/ProfilingTutorial/ 页。
- 单击 "OK"(确定)按钮以将 "NetBeans User" 作为您的用户名重新提交。需要稍长一点的时间才会出现响应,因为 Profiler 正在监视 processRequest 方法的性能。
- 在浏览器中显示 Hello, NetBeans User! 后,单击
按钮,或者选择 "Profile"(性能分析)> "Get Current Results"(获取当前结果)。
解释性能分析图形
Profiler 显示最新的性能结果,如下图所示。
顶部窗口显示完整的方法调用图形,从根方法开始。processRequest 方法的运行时间为 168 毫秒 (ms)。但要注意,运行 processRequest 方法本身的指令所花的时间很少 - 窗口中的第 6 行显示 processRequest 的 "Self time"(自用时间)仅为 0.421 毫秒。大部分时间都花在了 processRequest 调用的方法上。尤其是,forward 方法占用了 84.7% 的执行时间。考虑到为 forward 方法分配的工作量,这就不足为奇了。
NetBeans Profiler 的强大之处在于,它可以帮助您找出代码中无法预料的瓶颈或妨碍扩展应用程序的瓶颈。请注意,createUniqueID 方法占用了 11.3% 的执行时间。您可以单击 createUniqueID 条目旁边的加号以分析时间究竟花在了什么地方。如果您检查 createUniqueID 的源代码,就会发现它使用了一种效率非常低的算法,因此,应重新设计这种算法。
底部窗口是重点描述部分 - 它显示了调用图形中的每个方法的 "Self time"(自用时间)。
监视创建对象的过程
切换到内存分析模式
Profiler 可以对 CPU 性能或内存使用情况进行详细分析,但不能同时执行这两种分析。要了解有关在 JVM 堆上分配对象以及进行对象垃圾回收的详细信息,您需要修改 Profiler 设置。
- 单击
按钮,或者选择 "Profile"(性能分析)> "Modify Profiling"(修改性能分析)。
- 单击 "Analyze Memory Usage"(分析内存使用情况)按钮。
- 选择 "Record both object creation and garbage collection"(记录对象创建和垃圾回收)单选按钮。
- 单击 "OK"(确定)按钮。
运行分析的应用程序
现在,您已选择分析内存使用情况,您需要使用 Web 应用程序来确定它是否有效地使用内存。
- 在 Web 浏览器中,单击 "Back"(后退)按钮以返回到 http://localhost:8084/ProfilingTutorial/ 页。
- 单击“确定”按钮以将 "NetBeans User" 作为您的用户名重新提交。
- 在浏览器中显示 Hello, NetBeans User! 后,单击浏览器的 "Back"(后退)按钮以返回到上一页。
- 重复 9 次第 2 步至第 3 步。本演示需要重复提交相同的用户名。
- 单击
按钮,或者选择 "Profile"(性能分析)> "Get Current Results"(获取当前结果)。
- Profiler 将在表中显示最新的内存分析结果。单击 "Class Name"(类名)列以按类名对行进行排序。
- 向下滚动查看表行,直到显示 org.me.hello.NameHandler 类的行。
解释内存分析图形
下图显示了 org.me.hello.NameHandler 类的统计信息。
这些列提供了对象分配和内存使用情况信息。
- "Total alloc. obj."(分配对象的总数)(最右侧)最容易理解。这是为该类创建的所有对象的总数。对于 NameHandler 类,该值为 10,这是在 Web 应用程序窗体上按 "OK"(确定)按钮的次数。
- "Allocated Objects"(分配的对象)是 Profiler 实际监视的对象数。在此示例中,在创建的 10 个 NameHandler 实例中,Profiler 仅监视其中的 1 个实例。缺省情况下,该数字约为分配的总对象数的 10%(因此,您看到的数字可能与图中所示的数字略有不同)。通过只监视一部分创建的对象,Profiler 可以显著减少它在 JVM 中产生的开销,这样,应用程序就可以按几乎最快的速度运行了。
- "Live Objects"(活动对象)是指仍在 JVM 堆中并因而占用内存的已分配对象数。
- 两个 "Live Bytes"(活动字节)列显示活动对象占用的堆内存量。一个列显示图形,另一个列显示文本。
- "Age Average"(平均生存期)值是使用活动对象计算的。每个对象的生存期是指它经历的垃圾回收次数。生存期总和除以活动对象数得到的结果就是平均生存期。
- "Generations"(年代数)值是使用活动对象计算的。与平均生存期相同,对象生存期是指它经历的垃圾回收次数。"Generations"(年代数)值是活动对象的不同生存期数。
您可以使用此信息帮助跟踪内存泄漏情况。"Generations"(年代数)值与解释监视器图形部分中所述的 "Surviving Generations"(存活的年代数)值相同,但仅限于单个类的对象数。更精细的监视可以帮助您找出浪费堆空间的特定对象。
但在此示例中,您甚至不需要使用"Generations"(年代数)就能找出 NameHandler 类的问题。“Total alloc. obj."(分配对象的总数)值为 10。Servlet 代码的目的是,在输入相同的用户名时重复使用 NameHandler 对象。显然,没有发生这种情况。错误出在了 processRequest 方法:用于检查 Map 中的条目的键与用于存储该条目的键不同。结果是出现非常典型的 Java 内存泄漏情况:将对象放在 Map 中,然后忘记处理它们了。
另一种了解应用程序行为的简便方法是,查看在代码中的什么位置分配了特定类的对象。这通常可以提供为什么对象仍保留在堆中的线索。如果您右键单击表中的任何类的条目并选择 "Show Allocations Stack Traces"(显示分配栈跟踪),NetBeans Profiler 将显示该类的栈跟踪。
要结束性能分析会话,请选择 "Profile"(性能分析)> "Detach"(中断连接)。
深入探讨
本教程仅粗略介绍了 Profiler 的很多强大功能中的几种。有关这些主题的详细信息,请参阅 NetBeans Profiler 帮助文件。
- 在分析 CPU 使用情况时,可以定义一个类过滤器以防止方法性能分析超出应用程序的范围(例如,java.* 中的所有方法)。这有助于将应用程序保持接近全速运行。
- CPU 使用情况分析可能会影响 JVM 的字节代码优化,从而导致报告的 CPU 时间与实际性能不相符。不过,可以使用一些方法尽量降低 Profiler 的影响。
- "Analyze Memory Usage"(分析内存使用情况)功能仅限于监视对象分配。这会降低 Profiler 对性能的影响。
- 在分析内存使用情况时,如果您发现问题似乎是由少数几个类引起的,您可以禁止监视所有其他类的对象。
- 可以使用某些功能监视应用程序的启动时间或应用程序中的特定代码片段。
- 它提供了详细的线程统计信息。
- 远程性能分析 - 分析在不同计算机上运行的应用程序。
来源:https://netbeans.org/competition/win-with-netbeans/nb-profiler-tutor-5_zh_CN.html?print=yes