目次


Runtime SpyでEclipseの起動パフォーマンスを調整する: 第1回

はじめに

Comments

新しい会社がEclipseをアプリケーション開発のプラットフォームとして選択したというニュースが毎日のように入ってきます。潜在的には(Eclipse幹事会社の製品は言うまでもなく)こうした会社の製品全てが同じインストールにまとまっている可能性を考えると、膨大なメモリが必要になったり、パフォーマンス低下が起きたりする危険性が高くなります。この記事では非常に便利でありながらあまり知られていないツール、RuntimeSpyを紹介することで、プラグイン開発者の助けにしたいと思います。RuntimeSpyのパースペクティブはコア・チームのSpies and Toolsグループの一部です(ダウンロード用のリンクについては参考文献を見てください)

注意: コア・ツールはEclipse version 2.x上でのみ実行します。この記事の発表時点ではEclipse3.0ドライバーの上では実行しません。バグ番号 47518がこの問題を説明しています。

なぜEclipseは素早く起動する必要があるのか

Eclipseのアーキテクチャーは、環境に対するエクステンションの発見を、実行時に動作可能にするように設計されています。こうして構成されたエクステンションの機能によって、多くのツールがEclipseの中に継ぎ目なく統合されます。Eclipseの設計者がプロジェクトの初期に気が付いたのは、Eclipseが次々にエクステンションを統合して行くと累積的な起動コストが大きくなりすぎるため、こうしたエクステンションをクライアントのコードでプログラム的に定義することはできないという点です。代わってこうしたエクステンションはプラグインとして定義されるようになりました。

柔軟性を維持しつつ起動時のコスト増大を避けるために、プラグインはそのエクステンションをマニフェスト・ファイルで統計的に定義します。プラグインのマニフェストは、Eclipseプラットフォームがエクステンションによる初期要因を認識しつつコードのローディングは後に延ばせるように、十分な情報を定義します。例えば、ユーザー・インターフェース・エクステンション・ポイントは初期ユーザー・インターフェース要素(例えばアイコンや、提供されるツールバー・ボタンのヒントの文)を描画するために十分な情報を要求し、それによってユーザーが実際にメニューオプションを選択し、ツールバー・ボタンを選び、プリファレンス・ページを開くか生成ウィザード(creationwizard)を起動するまで、プラグイン・コードのローディングが遅らせられるようになります。プラグインの初期コストはマニフェストを構文解析することだけです。XMLフォーマットは素早く構文解析され、その結果は次回のためにディスクに保存されます。ですから起動は新しいプラグインやエクステンションが定義されても大きく影響されることはありません。ところがこの利点が知らないうちに無効になり、起動時間やメモリ消費の増大を引き起こす場合があるのです。

幸いEclipseプラグインの開発者は、こうした問題を追跡するためにRuntime Spyパースペクティブを使うことができるのです。この記事ではコア・ツール・プラグインとそのRuntimeSpyを紹介し、加えてreadme(参考文献)に説明されている以外の、ちょっとしたヒントを説明します。このシリーズ第2回では、起動時間の増大につながる一般的なプログラム間違いについて説明し、またRuntimeSpyを使ってIBM® WebSphere® Studio Application Developer version5.1.1の起動パフォーマンスをどのように改善したかについても説明します。

コア・ルールをインストールする

インストールは単純です。単にCore Tools zipファイルをダウンロードし、<inst_dir>\eclipse\pluginsディレクトリにunzipします。次にベースのEclipseインストールをスパイするのか(-debugコマンドライン・オプションを使います)、ランタイム・ワークベンチをスパイしたいのか(その起動設定のTracingページを使います。これについては「ランタイム・ワークベンチでのスパイ」で再度触れます)を決めます。ここでは前者で行くことにし、ベースEclipseインストールをスパイします。

まずSpyのオプションをすべて使用可能にするために、サンプルの.optionsファイルをplugins\org.eclipse.core.tools_1.0.2サブディレクトリから自分の<inst_dir>\eclipseディレクトリにコピーします。これでクラス・モニター用以外の全オプションが使えるようになります。クラスのローディングをモニターするには、plugins\org.eclipse.core.boot_1.0.2\trace.propertiesファイル中で関心のあるクラスを含むパッケージやプラグインをリストアップする必要があります。これらをどのように指定すべきかについては「どのプラグインがアクティブになっているかを見る」を見てください。

次にEclipseを起動し、忘れずに-debugコマンドライン・オプションを指定します。これで<inst_dir>\eclipseディレクトリにある.optionsファイルが読み込まれます。こうする代わりに-debugオプションのパラメーターとして、.optionsファイルの位置を指定することもできます(例えば-debug file:d:\...\.options)。

ワークベンチをスパイする

コア・ツールのファイルをインストールしてEclipseを再起動したとしましょう。-debugコマンドライン・オプションが指定されているので、stdoutに一部の起動メッセージがあるはずです。これらはデバッグ・モードでは別のコマンド・プロンプト・ウィンドウに表示されます(図1)。

図1. -debugオプションを指定するとstdoutメッセージを表示するコマンド・プロンプト・ウィンドウが開く
図1. -debugオプションを指定するとstdoutメッセージを表示するコマンド・プロンプト・ウィンドウが開く
図1. -debugオプションを指定するとstdoutメッセージを表示するコマンド・プロンプト・ウィンドウが開く

全てが立ち上がったので、Runtime Spyのビューをちょっと見てみましょう。Spyは「スパイされている」プラグインと同じワークベンチで実行するので、このツール自体を普通に使っている中で一部のプラグイン起動が起きる場合があることは頭に置いておいて下さい。これは普通、問題になりません。このプラグイン起動は、おそらく既にロードされているか間もなくロードされるはずだったベース機能を使うにすぎないからです。稀な場合として問題になる時には思い出して欲しいのですが、このビューは要求によってのみ更新されるので、最初にRuntimeSpyパースペクティブが開いた時には、自身で起動する前に既に起動していたプラグインのみを表示します。

どのプラグインがアクティブになっているかを見る

Window > Open Perspective > Runtime Spyを選択すると4つのビューが開きます(図2)。

図2. Runtime SpyパースペクティブはActivated Plugins、Loaded Classes、PluginDatasheet、Stack Trace viewsから成っている
図2. Runtime SpyパースペクティブはActivated Plugins、Loaded Classes、PluginDatasheet、Stack Trace viewsから成っている
図2. Runtime SpyパースペクティブはActivated Plugins、Loaded Classes、PluginDatasheet、Stack Trace viewsから成っている

-debugオプションを指定し忘れるとActivated Pluginsビューに「Plugin monitoringis not enabled」というメッセージが表示されます。デフォルトではクラスのモニタリングは無いので、LoadedClassesビューには「Class monitoring is not enabled」メッセージが表示されています。クラスのローディング情報をキャプチャーするとEclipseが遅くなります。ですから、関心のあるクラスを含むパッケージやプラグインを指定することで、どのクラスに関心があるのかをリストアップする必要があります。ここではただ単に、どんなプラグインがロードされているかのみを見ています。図3はRuntimeSpyの中心的なビュー、Activated Pluginsです。

図3. Runtime Spyに見るActivated Pluginsビュー
図3. Runtime Spyに見るActivated Pluginsビュー
図3. Runtime Spyに見るActivated Pluginsビュー

Runtime Spyビューにあるメモリ使用統計(Alloc、Used、Rom Used列)が必要であれば、IBMJava Runtime Environment with J9技術を使う必要があります。このJREはWebSphere®Studio Workbench と呼ばれるIBM®バージョンのEclipseに含まれています。これはIBMPartnerWorld for Developersに登録すれば無料でダウンロードすることができます(参考文献にリンクがあります)。Javaランタイム環境の引数としてJ9を指定するのを忘れないでください(例えばeclipse -debug-vmargs -Xj9)。

最初の列の見出し、Pluginをクリックすると、並べ替えを昇順、降順、グループ分け昇順、に切り替えます。「+」記号でグループ化されたプラグインは必須のもので、親の起動中に起動したプラグインです。ロードされたプラグインがグループ化されると、行の値はそのプラグインとその子としてのプラグインのものになります。この順序付けは、大量に使用しているものをグループとして見たい時に使用します。

プラグイン名の後にアスタリスクがついたものは起動中にロードされたものです。名前からの連想に反して、ActivatedPluginsビューにある、アスタリスクがついた起動プラグインには、ワークベンチがorg.eclipse.ui.startupエクステンション・ポイントを処理する結果ロードされるものは含まれません。つまりワークベンチはこうした、エクステンションによる要因を最初の起動の後に処理するのです。

特に面白いのはOrder列です。列の見出しをクリックすると、プラグインのリストをロード順に並べ替えます。あるアクションでどのプラグインがアクティブになったかを素早く見たい場合には、先に全てのプラグインを選択し(Ctrl+A)、そのアクションを実行し、ActivatedPluginsビューに戻り、次にRefreshボタンを選択します。選択されていないプラグインは、今ちょうど起動されたプラグインです。あるいは、そのアクションの前に起動した最後のプラグインの順番の値をメモしておき、アクションの後で更新をかけ、順番の値がメモの値より大きいプラグインを見ます。

プラグインのどんなクラスがロードされているかを見る

プラグインのクラスは要求によってロードされます。こうした参照を後に延ばしたり、クラスの基準セットを減らしたりすることでメモリや起動時間を減少できる可能性があります。LoadedClassesビューで、選択されたプラグインのどんなクラスがこれまでにロードされているかを見ることができます。LoadedClassesを更新するには、Activated Pluginsリストから一つ以上のプラグインを選択してからClassesボタンを選択します。図4はorg.eclipse.jdt.coreプラグイン用にロードされているクラスをロード順に並べたものです。

図4. Runtime SpyでのLoaded Classesビュー
図4. Runtime SpyでのLoaded Classesビュー
図4. Runtime SpyでのLoaded Classesビュー

このビューでは、プラグインを起動する順で並べ替えるのに加えて参照の順で並べ替えることにより、あるアクションがどんなクラスやシーケンスを引き起こすかを「大きな目で」見ることができます。プラグインの起動コードがこのリストに含まれているので、そのコードを実行することによるコストがどの程度かが分かるのです。その結果分かるのは、多くの場合に「起動時に行われることが多すぎる」ということです。

なぜそのクラスがロードされたかを追跡する

あるプラグインがなぜ起動されたのか、または、あるクラスがなぜロードされたかを知るにはまず、対象とするプラグインやクラスのパッケージに対するトレース・オプションを使用可能にしておく必要があります。この例では、packages=org.eclipse.jface.textという行を含むtraces.propertiesファイルを作りました。次に以下が必要です。

  1. Activated Pluginsのリストにあるorg.eclipse.jface.textプラグインを選択する
  2. Classesボタンを押してLoaded Classesリストを更新する
  3. org.eclipse.jface.text.ITextViewerクラスを選択する
  4. Traceボタンを選択してStack Traceビューを更新する

これでクラスローダーがそのクラスをロードしたのはどんなコードによるものなのかが表示され、また、選択されたプラグインがまだ起動していなければ起動します。これを図5に示します。

図5. Runtime SpyでのStack Traceビュー
図5. Runtime SpyでのStack Traceビュー
図5. Runtime SpyでのStack Traceビュー

スタックのトップはクラスローダー・コード自体のトレースなので、一般に面白いものではありません。有用な情報は中程から下にあります。この場合のスタック・トレースを見ると、(ハイライトされたスタック・トレース行の一番下に見える通り)RuntimeSpyパースペクティブが開かれていることから、最終的にITextViewerクラスがロードされたことが分かります。このパースペクティブはその初期ビューを開いています。このビューにはPluginDatasheetビューも含まれていますが、これはJFace TextクラスTextViewerを使ってパースペクティブのデータを表示します。JVMがdefineClassでこのクラスを検証していると、TextViewerITextViewerを実装していることから、JFace TextクラスにはITextViewerも必要なことを見つけます。ご覧の通り、JVMによるランタイムのクラスローディングは非常に深くネストされています。パフォーマンス向上のためには普通、クラスローダー・コールに至るまでのコードに注目します。これは図5でハイライトされているスタックトレース部分と似たものになります。

なぜプラグインがロードされたかを追跡する

この前の例では、ある特定のクラスがなぜロードされたかを説明しました。なぜプラグインがロードされたかも分かると思いますが、あるプラグインがなぜ起動されたかは、その原因が間接的なので、あまり明確ではないかも知れません。クラスがロードされるのは普通、別のクラスのメソッドで参照されている(そして参照可能な、対応したimport文がある)ためですが、プラグインはクラスと違って何か間接的な参照の結果ロードされます。必要になるまでプラグインをロードしないようにすることが目標であることを思い出してください。ですからプラグイン自体への参照は次のようなものによることになります。

  • プラグインのマニフェストにある<import plugin="org.eclipse.ui">のように明示的に表記された識別子、または
  • そのプラグインに対するランタイムJARにある、エクスポートされたパッケージのような暗黙識別子

どちらの例も「Hello, Eclipse」プラグインのマニフェストの抜粋にハイライトしてあります(リスト1)。

リスト1. 「Hello, Eclipse」のエクステンション・ポイントの例
<?xml version="1.0" encoding="UTF-8"?>
<plugin ...>
... lines omitted ...
   <runtime>
<library name="hello.jar"/>
         <export name="*"/>
      </library>
   </runtime>
   <requires>
      <import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.ui"/>
   </requires>
... lines omitted ...
   <extension point="org.eclipse.ui.actionSets">
      <actionSet
         label="Sample Action Set"
         visible="true"
         id="hello.actionSet">
         <menu
            label="Sample &Menu"
            id="sampleMenu">
            <separator
               name="sampleGroup">
            </separator>
         </menu>
         <action
            label="&Sample Action"
            icon="icons/sample.gif"
class="hello.actions.SampleAction"
            tooltip="Hello, Eclipse world"
            menubarPath="sampleMenu/sampleGroup"
            toolbarPath="sampleGroup" id="hello.actions.SampleAction">
         </action>
      </actionSet>
   </extension>

org.eclipse.ui.actionSetsエクステンション・ポイントに対して行われたアクションの要因は、このエクステンション・ポイントを定義するプラグイン、つまりWorkbenchUIプラグイン(org.eclipse.ui)に暗黙的に依存しています。このワークベンチ・プラグインはプラグインのレジストリを読みますが、それにはorg.eclipse.ui.actionSetsエクステンションへのこの要因が含まれ、適切なアクション・セットを作ります。SampleActionクラスはこの時点ではロードされないので、そこに含まれるプラグインもロードされません。代わりにワークベンチ・プラグインはユーザー・インターフェースでの選択を表わす代行アクションを定義します。このインターフェースはレスポンスを処理するためのSampleActionのインスタンスを生成する前に、ユーザーが実際にそのアクションを選択するまで待ちます。

SampleActionのインスタンスを作るには、アクション代行が、我々のマニフェストファイル例で規定されている<action class="hello.actions.SampleAction" ...>タグを表すプラグイン・レジストリ・インスタンスIConfigurationElementcreateExecutableExtensionメソッドを呼びます。それはそれで良いのですが、その結果のスタック・トレースを見るだけでは明確ではない場合が時々あるのです。「Hello,Eclipse」メニュー選択の例よりも難しい例で詳しく見て行きましょう。図6は実行可能エクステンションの処理の結果による起動の典型的な例です。では今度はStackTraceビューを使い、前と同じステップに従って、何によって起動されたのかを調べてみます。

図6. 実行可能エクステンションの起動を示すStack Traceビュー
図6. 実行可能エクステンションの起動を示すStack Traceビュー
図6. 実行可能エクステンションの起動を示すStack Traceビュー

この場合では、スタック・トレースでハイライトされた部分の一番下を見ると、PDEのPlug-insビューでダブル・クリックしたところから全てが始まっていることが分かります。このアクション処理に責任のあるデフォルトのアクション・ハンドラーがワークベンチに対してプラグイン・マニフェスト・エディターを開くように要求しており、これが結果としてorg.eclipse.ui.editorsプラグインを起動しています。スタック・トレースでハイライトされた部分はエクステンション・ポイント・プロセッサーのコードのみを示していますが、これはこの部分のワークベンチのプラグイン・クラスorg.eclipse.uiへの参照が間接的なためです。似たようなスタックトレースを5,6種類相手にしてみると、IConfigurationElement.createExecutableExtensionへのコールの前後で重要な部分が分かるようになり、また誰が始めたものか、その結果何が起きたのかがすぐに分かるようになるでしょう。ロードされているクラスを更新するためにClassesボタンを再度選択し、ロード順で再度並べ替えを行うと、プラグインが起動した後に何が起きたのかがよく分かります。

他の有用なビュー

最後に、Plugin Datasheetビューは、例えばプラグインがどれほど多くのリソースやエクステンションを定義しているか、といったような面白い統計を見せてくれます(図7)。

図7. 使用されているリソースをPlugin Datasheetで表示する
図7. 使用されているリソースをPlugin Datasheetで表示する
図7. 使用されているリソースをPlugin Datasheetで表示する

これはIPluginDescriptor.getResourceStringメソッドとそのvariants を通してロードされたResourceのバンドル・データを追跡しています。この要約情報はEclipseプラットフォーム・ランタイムには独自のクラスローダーが用意されていること、またクラスローダーはリソース・バンドルをクラスと同じように扱うという事実を利用しています。ですからリソースデータ統計の追跡は単純なのです。「notloaded yet」メッセージが言っているのは、プラグイン・レジストリはディスクに書き込まれており、その中で参照される部分は必要が起きた時のみ再ロードされる、ということです。

ランタイム・ワークベンチでのスパイ

これまでのセクションではEclipse自体のベース・インストールについてのスパイについて説明しました。より現実的には、ランタイム・ワークベンチとして知られている、テスト版のワークベンチをスパイしたいでしょう。起動したいランタイム・ワークベンチのインスタンスを設定するには、Run > Run As... > Run-time Workbenchを選択し、Tracingページを開きます(図8)。

図8. Tracingページでランタイム・スパイ・オプションを設定する
図8. Tracingページでランタイム・スパイ・オプションを設定する
図8. Tracingページでランタイム・スパイ・オプションを設定する

org.eclipse.core.bootプラグインのデバッグ・オプションには先に説明した<inst_dir&gt\eclipse\plugins\org.eclipse.core.boot_2.1.1\.optionsファイル選択が入っています。これは読者がまず確実に必要とするデフォルトを定義しています(全てがオン)。ところがまずまずの精度で経過時間のパフォーマンス測定をしようとするのであれば、アクティブなオプション、特にスタック・トレース(つまりtrace/pluginactivationなど)を要求するようなものの数は最小にすべきです。monitor/pluginsを真(true)に、そして他の全てを偽(false)に設定してもパフォーマンス・オーバーヘッドはほとんど増えません。

これから先は

Javaパフォーマンス調整については多くの情報がありますが、Eclipseに特定したものはほとんどありません。この記事では最善のツールの一つとして、プラグイン起動に関連した起動パフォーマンスの問題を理解し、診断するためのものを紹介しました。最後にコアツールにある診断情報として、他にどんなものが有用かを挙げておきましょう。

  • Plug-in Dependencyパースペクティブでは、Plug-in Registryビュー(<Window> Show View > Other... > PDE Runtime > Plug-in Registryから行かれます)で見える情報に似た情報が見られます。ただし、選択したプラグインの従属プラグインが延々と続くリストが付いてきます。
  • ワークスペースの .metadataディレクトリに何があるのか疑問に思ったことがありませんか?Metadataパースペクティブを使えばその構造を調べることができます。ただしこれを使うにはワークスペース実装をかなり理解している必要があります。
  • Resource Toolsカテゴリーのビューを使うと、リソース変化リスナー、リソース・デルタ、ビルダー等々の内部動作がよく分かります。特にResource、DeltaそれにBuilder/Listener等のビューはワークスペースAPIを学んでいる人にとっては興味深いものです(このビューに行くにはWindow > Show View > Other... > Resource Toolsを選択します)。

これらのツールについてはさらに、コアツールのreadmeでも知ることができます(参考文献

このシリーズ第2回では、私がWebSphere Studio Application Developer version 5.1.1で起動の問題をいくつか診断するためにRuntimeSpyをどのように使ったかを紹介します。対策の結果、アクティブなビューやパースペクティブにもよりますが、起動パフォーマンスは11%から37%改善しています。この結果から、プラグインがいつ、なぜ起動されるのかを知っていれば、Eclipseの約束する素早い起動が実現できることが分かるでしょう。

そして、バージョン6のWebSphere Studioができるまで待ってください。これで本当に起動パフォーマンスが改善するのです!


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Java technology
ArticleID=236862
ArticleTitle=Runtime SpyでEclipseの起動パフォーマンスを調整する: 第1回
publish-date=03042004