当前页面: 开发资料首页 → Eclipse 专题 → 用 Runtime Syp 调整 Eclipse 的启动性能,第 2 部分
摘要: Runtime Spy 是 Eclipse.org 提供的核心工具 (Core Tools) 之一,它是特别设计的一个透视图及一组视图,用于帮助您找到并诊断插件启动性能问题。本文介绍了如何发现可能会导致启动时间过长的常见设计错误,以及如何去纠正它们。其中的一个案例研究说明了 Runtime Spy 如何用于提高 IBM WebSphere Studio Application Developer 的启动性能。上一篇文章,也就是第 1 部分,对 Runtime Spy 进行了介绍。
阅读完本系列文章的 第 1 部分 后,您应该已经对 Runtime Spy 如何来帮助您查找启动问题的位置有了大体的认识。让我们通过一些特定的例子来弄明白如何用它减少您的插件启动时间。为了让话题更有可读性,我们将探讨一些在 Runtime Spy 帮助下纠正的问题,这些问题的纠正是 IBM WebSphere Studio Application Developer 性能提高的一个方面。
注意:核心工具只能运行于 Eclipse 版本 2.x。在本文发表时,它们还不能运行于 Eclipse 3.0 驱动程序上;编号为 47518 的 bug 描述了这一问题。
帮助 Eclipse 快速启动
为提高一个基于 Eclipse 的应用程序的启动性能,一般来说有两个目标:
这两个目标共同的原则是 尽可能延迟代码的执行。您可以采用的一些方法:
Plugin.startup
方法中的引用。很多插件重载这个方法以完成它们的初始化。理想的解决方法通常是,让您的插件延迟它的初始化,直到用户请求您的产品的某个特定的动作时再初始化。要不然,下一个最好的选择是最小化引用的类和插件的数量。在任何一种情况下,Runtime Spy 都可以指出哪里可能会占用太多的时间或者触发太多其他插件的激活。
startup
方法的代码或者它所调用的代码。内存结构的滞后初始化可以节省 CPU 时间并延迟其他插件的激活。还有一种可能是在启动时派生一个单独的低优先级的线程,当系统空闲时再去完成初始化,不过这种方法需要特别注意处理好同步。
通过延迟插件的激活,会给用户一种产品更为灵活的印象。尽管 累积的CPU 时间是完全相同的,但是相对于在最开始强制完成,将其分解为若干小块在较长的一段时间内分步完成会进一步降低人们的注意力。在第一次调用时(也就是启动您的产品或者打开第一个透视图、编辑器或视图时)尤其不应该有延迟,因为正是在这个时候用户最关注于工作的完成而没有太多耐心。
再介绍 Runtime Spy
Runtime Spy 为您提供了用于跟踪三种加速启动方法的基本统计表,如图 1 所示。
您的第一个目标是让 Activated Plugins视图中出现的条目减到最少。对于那些出现在列表中的插件,您的第二个目标应该是让 Loaded Classes视图中出现的条目减到最少。Activated Plugins 视图中的 Startup time列将为您指出那些启动时间较长的插件。在 Activated Plugins 中选择 按钮可以更新 Stack Trace视图,让您明白 插件 为何被加载,在 Loaded Classes 视图中选择 按钮将为您说明 类为何被加载。
侦探 WebSphere Studio
让我们以一个如何使用 Runtime Spy 来诊断和纠正启动代码错误的例子来开始。如您将看到的,这些修正不会在绝对意义上“提高”启动性能,而是延迟了启动开销,这样会让用户感觉产品整体上更快。底层的 Eclipse 产品在这一点上做得非常好,它会随着您的使用而将启动开销分解为小块分阶段进行。这是 Eclipse 的延迟插件加载策略的基础。
为对情况进行第一次观察,让我们启动 Studio 时只打开 Runtime Spy 透视图。这样应该会加载几乎最少的插件并以最快的速度启动,如图 2 所示。
按下 按钮不会改变 Activated Plugin 列表中的已作出的选择。如果您想查看特定的动作激活了哪些插件:
没有被选中的条目就是新激活的插件;被选中的那些是在动作之前就已经被激活的。另外一种方法是,您可以记下 Order列中最后加载的插件的编号,执行动作,然后对 Order 列重新排序,查看哪些插件出现于先前(已经激活)的最后一个插件之后。
</td></tr></table></td></tr></table>注意插件名末尾的星号。这些插件是在负责启动 Eclipse 的插件被激活时加载的,那个插件名为
application plug-in。这个插件提供了一个实现
org.eclipse.core.runtime.applications
扩展点的
IPlatformRunnable
接口的类。默认地,Workbench UI 插件提供对这个接口的一个实现,来创建工作台窗口,收集对主菜单栏、工具栏等等的动作贡献 (action contributions),一般情况下还要为事务处理准备好 Eclipse 工作台用户界面。
实际上消耗的时钟时间将会比 Activated Plugins 视图中显示的 Startup time 列的和要多。这是因为后者不包括 Platform Runtime 加载之前 JVM 的运行时间或者插件启动之外的 CPU 运行时间。在图 2 所示的例子中,从启动配置启动 Run-time Workbench 时起实际上的启动时间大约是 13 秒。其中还包括启动配置本身为构建插件列表等任务而引入的程序调试时间 (development-time) 开销。
即使加上这些开销,启动时间还是非常少,不是吗?
Workbench 启动扩展点的结果
让我们按下 按钮来更新活动的插件列表。就是那样!图 3 中是我们的第一个困惑。
被选中的那些插件是初始化时激活的。那么另外 11 个插件呢?或许有一些是因 Runtime Spy 透视图而加载的。更为有趣的是,图 3 中有一些看起来可疑的 Studio 插件。列表顶部显示的是“cheatsheet”和“internet”。它们是什么?为什么现在被加载?回到用户界面,我们在 Help下拉菜单下找到了与第一个插件相关的选项,如图 4 所示。
一般只有新用户才会去选择 Cheat Sheets菜单选项,所以我们为什么还要在启动时为加载它们的相关插件而付出代价呢? Workbench > Startup首选参数页提示了答案,如图 5 所示。
org.eclipse.ui.startup
扩展点可能是版本 2.0 一直有争议的 API 之一。当工作台窗口打开时它可以全权要求 Workbench UI 激活插件,而不受延迟加载策略限制。如果一个插件的清单定义了这一扩展点,那么它的插件类必须实现
IStartup.earlyStartup
方法。
有对这个 API 的合法使用。由于文章长度有限,不能尽述这个列表中每一个条目。所以让我们来考虑如上面图 5 所示添加的两个插件的合法性:
URL
类的系统属性初始化
Window
> Preferences > Internet设置。由于没有办法可以知道这个类什么时候会被引用,并且因为没有显式的初始化方法,这样做看起来是合法的。不过,考虑使用库扩展可能是明智的。我们稍后将回来解释原因。
首先您可能会疑惑为何 Workbench 会定义一个
Startup 首选参数页。可以取消选择列出的插件之一,让用户有机会选择用减少的功能换取更快速的启动。例如,有经验的开发者不需要
Cheat Sheets 层叠菜单提供的提示,可以选择禁用它对
org.eclipse.ui.startup
扩展点的贡献,这样就删除了
Help > Cheat Sheets 菜单选项。如果您自己的插件对这个扩展点有贡献,那么要记住这一点。也就是说,要保守地编写您的插件代码,假定插件类的
IStartup.earlyStartup
方法可能还没有被调用。
回到我们在图 3 中的例子,考虑列表中没有星号的插件
com.ibm.etools.internet
。图 6
中它的栈记录确定了为什么这个插件在工作台窗口打开后被激活。
Workbench 的
run
方法被高亮了,对扩展点的贡献在这里被处理。自此以后激活的插件或者是对
startup
扩展点有贡献,或者是对其中的引用有贡献。
找出可能的 WebSphere Studio 热点
让我们打开 J2EE 透视图来查看一些 Studio 特定内容。(如果您想诊断一个简单些的情形,那么打开一个单一的视图,比如 Window > Show View > DB Servers,而不要打开相应的透视图;那样已激活插件的列表会短很多。)打开 J2EE 透视图后,回到 Runtime Spy 并选择 Activated Plugin 的 按钮。 哇!列表中的插件数从 22 跳到了 73,增加了 20 秒的插件激活时间(先前总的时间也只有三秒多)。图 7 的显示中以插件的激活顺序对它们进行排序,最近激活的插件显示于顶部。
公平地讲,Runtime Spy 增加了开销,尤其是当需要捕获栈记录时,所以从半热态启动 (warm start) 时,实际上不加修正 (uninstrumented) 消耗的总的时间接近 37 秒。但是让我们来看一下是否有一些启动插件之外的插件有可能不必启动。为节省空间,下面这个列表并不完全,因为我们认为基本的组件比如 EMF、JDT 和 J2EE UI 是需要的。但是这些呢?
到现在并没有与数据库相关的视图或者编辑器被打开。而为什么关系数据库模式中心 (6) 和那么多其他数据库相关的插件被激活了?插入的“可以被延迟”注释表示 Eclipse 有能力以体系化的解决方案使它们在需要之前不被激活,这只是关于属性的一种特殊情况。但是,即使全面地考虑,这些大部分都是无足轻重的。然而,还是有一些插件消耗了大量的启动时间,而且,如我们所看到的,它们的开销与处理扩展点期间插件激活紧密联系在一起。下一节更全面地解释了这一开销,并对启动开销是如何产生的进行了研究。然后我们将返回到我们对 J2EE 透视图启动的分析。
理解插件激活与扩展点处理之间的关系
通过避免对
Studio 的源代码不需要去理解发生了什么;栈记录就足以指出原因。Eclipse 类
注意对
插件的类加载器 (classloader) 负责在它的库中解析对类的引用。类加载器将插件引用映射到它们相应的包含有插件类的 JAR 文件,比如我们的例子,
避免与扩展点处理相关的过早的插件激活
回到图 8 中所示的栈记录,我们可以发现,在所选择的文本中部的代码通过调用
实际上所有会激活另一个插件的类最终都可以追溯到对
就参数而言,这个扩展贡献看起来类似于用来定义菜单选项贡献的
结束语
现在您应该知道为什么本文的子标题定为“成功的骗局”了。这完全是为了满足用户的期望,并让用户不去理会您希望他们不要去注意的东西。在这个特例中,现在启动更快了,但是稍后如果用户启用数据库工具,他们还要是付出同样多的
CPU 开销。那是可以接受的,因为开销是以化整为零的方式付出,而且,更重要的是,它使延迟更接近用户期望付出代价的相关动作。 记住,目标是
尽可能延迟代码的执行,不仅因为它可以带来感觉上性能的提高,还因为最容易用于优化速度的代码是永远不执行的代码。Eclipse
的插件体系结构让您可以容易地创建达成此目的的扩展,Runtime Spy 帮助您找到那些可以对实现进行改进的情形。
关于作者<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td colspan="3"></td></tr><tr align="left" valign="top"><td></td><td></td><td width="100%"> Dan
Kehn 是美国北卡罗莱纳州 Research Triangle Park 的 IBM 高级软件工程师。他对面向对象编程的兴趣要追溯到 1985 年,当时这种技术还不像现在这样广为接受。他拥有广泛的软件经验,从事过开发工具(如 VisualAge for Smalltalk)、操作系统性能和内存分析和用户界面设计。Dan
作为面向对象开发项目的顾问走遍了美国和欧洲。他最近的兴趣包括面向对象分析/设计、应用程序开发工具,以及使用 WebSphere Application
Server 进行 Web 编程。他是
The
Java Developer's Guide to Eclipse
一书的合著者。
subuilder
插件(按它的插件清单的说法,它是
Stored Procedure和
UDF
Builder)的激活来节省启动时间看起来可以成功,因为它需要用去总时间的 10%。此外,它几乎是在激活序列的最后(73 个中的第
72 个),所以,进行“修剪 (trim off)”可能要比避免次序靠前的插件激活更为简单。这个插件激活的栈记录见图 8。
PartPane
管理您看到的那些选项卡视图,包括
J2EE Hierarchy、
Package Explorer和
Navigator 视图。如图
8 所示,在选择的文本下方,选项卡视图
PartPane
正在尝试创建与
J2EE Hierarchy
选项卡相关联的视图的一个实例,类的名字为
J2EEView
。进一步查看记录的上方,我们看到这个类反过来调用了一个帮助者
(helper),然后这个帮助者调用了
createExecutableExtension
方法。
IConfigurationElement.createExecutableExtension
方法值得特别注意。当您调试您自己的性能问题时,您将会发现,这个方法导致了 Rumtime Spy 没有发现的很多插件的激活案例。为更好地理解它做了些什么以及它如何影响插件激活,考虑清单
1 中给出的简单的扩展点贡献。您可能会发现,这与
第
1 部分中规范的“Heloo Eclipse”样例的清单是相同的。
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
<?xml version="1.0" encoding="UTF-8"?>
标签的
class
属性的处理。这指定了将来处理菜单选项选择的类的名字,这个类必须实现
IWorkbenchWindowActionDelegate
接口。这个类并不位于处理
org.eclipse.ui.actionSets
扩展点的同一个插件中(Workbench UI 插件),也不位于它的先决插件中。那么,当它看起来不取决于插件的 classpath 时,Workbench
UI 插件如何去创建这个类的一个实例呢?
SampleAction
。因此,Workbench
UI 插件不必“知道”通过属性引用的类,比如
标签的
class
。相反,是由于“Hello
Eclipse”插件定义了对 Workbench UI 插件的依赖而使之可以访问目标插件的类,因而有权通过目标插件的类加载器访问 Hello
Eclipse 的类。简洁地重述一遍,当 Eclipse 找不到某个类时,它会要求目标插件的类加载器去加载那个类;那就是
createExecutableExtension
方法所做的事情。
createExecutableExtension
对扩展贡献进行处理 —— 看吧! 插件开始加载了。我们现在要寻觅的是明摆着的问题的答案:这时需要加载它吗?可以将它的加载向后延迟吗?
createExecutableExtension
方法的调用。这些调用的调用参数指明了属性名(通常是
“
class
”)和它的值,即要加载的类的全名。这就带我们回到了导致激活(此插件)的插件扩展贡献,在我们的例子中也就是
subuilder
插件。清单 2 是它的清单的摘录。
<table bgcolor="#eeeeee" width="100%" cellpadding="5" cellspacing="0" border="1"><tr><td>
</td></tr></table>
org.eclipse.ui.popupmenu
扩展点;更确切地说,它定义了一个标签(属性“name”),位置(属性“group”),目标(属性“view”),还有最特别的,处理程序(属性“class”)。Eclipse 扩展与这个特殊实现的区别是,Eclipse 的扩展创建一个委派类作为菜单选项的代理。直到菜单选项真正被选择时,相应的处理程序类才会被创建,因而延迟了包含此处理器类的插件的激活。将同样的代理策略应用到前面的例子中,J2EE 透视图的打开快了差不多 7 秒。创建
NewSQLSPAction
实例和激活包含它引用的类的插件的开销并没有消除,而是改变为随取随付 (pay-as-you-go)。
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td>
</td></tr></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tr align="right"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr><td valign="middle">
</td><td valign="top" align="right"></td></tr></table></td></tr></table>
↑返回目录
前一篇: 用 Runtime Spy 调整 Eclipse 的启动性能,第 1 部分
后一篇: 驾驭 Eclipse 功能部件