Quest 社が JProbe プロファイラの無償バージョンを発表したことは、私にとって一大ニュースでした。 半年ごとに新しいライセンスをダウンロードしないといけませんが、大した手間ではありません。 含まれているのはメソッドプロファイラだけで、スレッドやメモリのプロファイラは外されています(つまりオブジェクトのプロファイリングはできません)。 また、スナップショットの比較は無効にされています。 それでもこれは相当にすごいことです。 プロファイラそのものの機能は削られていません。 この発表があまり注目を集めなかったことには驚いています。 ともかく、このニュースレターを購読するほど賢明なみなさんは、こうして知ることができたわけです。 発表資料へのリンクは後ろの「ニュース」セクションにあります。 「ツール」セクションにはダウンロードページへのリンクもあります。
これが JProbe の潜在的需要を食ってしまうかどうか、私にはなんともいえませんが、たぶんどうということはないでしょう。 Javaアプリケーションのメソッドプロファイリングについては、どんどん選択肢が増えています。 私たちのお客様、教育コースの受講者各位、JavaPerformanceTuning.com 読者のみなさんからいただくご意見を見ると、負荷の高いサーバのメモリリーク追跡やメソッドプロファイリングが問題になってきているようです。 こうした機能は無償版には含まれていません。 また一方で、JProbe は安価または無料のプロファイラを探している人の玄関にうまく足を踏み入れたとも考えられます。 ですので、マーケティング上はかなり良い戦術と言えるでしょう。
一つだけ落とし穴があります。私が知る限り、Quest はこれが将来にわたって続くとは発表していません(何かわかったらお知らせします)。 最低でも6ヶ月ごとにライセンスを更新しないといけないという要件は、すなわち Quest はいつでもフリーウェアの機能を止められる、そして6ヶ月以内にフリーウェア版はみんな動かなくなる、ということです。 気が付いたら、頼りにしているプロファイラが突然代金の支払いを要求する、あるいは別のツールに習熟しなおす羽目になるかもしれません。 読者各位には影響はないかも知れないし、大いに影響があるかも知れません。 いずれにせよ、少なくとも知っていて損はないでしょう。
ニュースレターではいつもどおり多数の記事、ニュースなどを用意しています。 さらに定番のセクションもほぼそろっています。 Kirk のまとめ ではキャッシュの実装、弱参照とソフト参照などを取り上げます。 Jive Software 社の創始者 Matt Tucker and Bill Lynch との今回のインタビュー は、読み書きが頻繁に行われるサイトやポータルに関わる方には必読の記事です。 今月の質問 では、Java は実行時最適化によって他の静的最適化を行う言語より高い性能を出せるのか、 という問いに答えます。
漫画の新作、 簡潔に抜き出された多数のパフォーマンス tips もあります。 ただ Javva The Hutt のファンのみなさんには申し訳ないのですが、Javva はご家庭の都合でお休みしています。
Java パフォーマンスチューニング関連ニュース
折に触れて、会ったとたんに自分の考え方や価値観に著しい影響を与えるような人に出会うという、幸運な機会を得ることがあります。 今週、ある通訳の人と出会ったとき、この機会が私にも訪れました。 (読者のみなさんを含め)一つの言葉しか話さない人々には理解に苦しむことかも知れませんが、複数の言語を話せるからといって、通訳ができるわけではないのです。 通訳には翻訳者とは異なる様々なスキルセットが求められます。
通訳に必要なプロセスを追うと、その理由がわかります。 まず特筆すべきなのは、通訳は多くの言語に対応できるかもしれませんが、訳出できるのは一つの言語だけということです。 たとえば、私があった人物はドイツ語と英語をフランス語に訳します。 フランス人が話しているときは、彼は止まります。 この手法には明らかな合理性があり(たとえばフランス語を話す人は、常に会話をフランス語に訳してくれる一人の通訳に耳を傾ければいい)、またパフォーマンスの面でも有利なのです。
通訳のプロセスでは、聞き取り、理解し、目的の言語で話すという能力が求められます。 これら3つの活動は同時に行われなくてはいけません。 通訳は会話の進む速度を制御できませんし、あまり遅れをとってしまうと会話の取りこぼしを避けられないからです。 この仕事では、間違いが許容される余地はほとんどありません。 これらすべての活動を行うには、通訳は自分の集中力をそれぞれに配分しなくてはいけません。 何かの理由でかれの集中力のバランスが崩れた場合、かれの集中力は一つの活動に集まりすぎ、残りの二つが影響を受けることになります。 このシステムでは、希少なリソース――この場合は脳ですが――の割り当てのバランスを正しく保つことで、適正なパフォーマンスが得られるのです。
この通訳の例では、圧迫されているリソースを見つけるのは簡単です。 アプリケーションの場合は、ここまで簡単ではありません。 アプリケーションを扱うときは、まず供給がとぼしいリソースが何かを特定しなくてはいけません。 それを見つけだすには、計測が必要です。 次のステップは、計測対象であるシステムのリソースの許容量を理解することです。 この結果として、どのリソースが圧迫されているのかがわかります。 また、次のステップである、どのようにこれらの希少リソースの利用を均衡させるかを学ぶことへの助けにもなります(それがそもそも可能なことであれば、ですが)。 この「学習」プロセスは非常に早く起きることもあります。 また、均衡に達するまでかなり長い時間がかかることもあります。 どちらにしても、通訳がこつを身につけるまでの5年間よりは短いことでしょう。
さて、それでは最近のディスカッショングループでの議論をレビューしていきましょう。
blog-city をチューニングする作業の中で、私たちはキャッシュ技法を使わざるを得ないような問題に突き当たりました。 最初に直感で、オープンソースのプロダクトを使うことにしました。 キャッシュの導入は多くの点で大惨事となりました。最もひどかったのはメモリリークを引き起こしたことです。 いくらかの時間を対応に費やしたあと、このプロダクトをはずしてキャッシュを作ることにしました。 もし私たちがゲームパフォーマンスのディスカッショングループを見ていたなら、まさに実装のガイドとなるような投稿をみつけていたことでしょう。 投稿そのものは簡単なものです。「古いものから順に削除する(LRU方式【Least Recently Used】)キャッシュの作り方を知りませんか?」 返事の中では、java.util.LinkedHashMap の javadoc を見るようアドバイスがありました。 ドキュメントには、このクラスを LRU キャッシュにするいくつかの手順が含まれています。 今度シンプルなキャッシュが必要になったときには、役に立つでしょう。
ServerSide からは、ソフト参照(soft reference)と弱参照(weak reference)に関する興味深い議論が出ています。 オブジェクトの持ち方には強参照、ソフト参照、弱参照、ファントム参照があります。 通常、オブジェクトは到達可能な強参照でメモリに結びつけられています。 オブジェクトが強参照で保持されており、リンクがルートとなるオブジェクトから到達可能である限り、これらのオブジェクトはガベージコレクションの対象とはなりません。 しかし、世界に色彩が加わるととたんにおもしろくなるように、1種類以上の参照をサポートする環境では話が変わります。 ソフト/弱参照を使って、私たちの開発人生をよりおもしろいものにしましょう。 強参照と同様、ソフト/弱参照は、オブジェクトがどこかにいるルートオブジェクトから到達可能であることを保証します。 強参照と異なるのは、ソフト/弱参照で保持されているオブジェクトはガベージコレクションの対象になるということです。
ソフト参照と弱参照の違いは、ガベージコレクション中の扱いにあります。 ソフト参照は参照しているオブジェクトをできるだけ長い間メモリに保持しようとします。 しかし弱参照はそうではありません。 言い換えると、弱参照に保持されたオブジェクトは何であれガベージコレクションの対象となるのです。 ソフト参照で保持されるオブジェクトは、OutOfMemoryExceptionをスローする以外にない、という状況でのみ回収されます。 これが本来あるべき挙動です。 残念なことに、このサブジェクトの議論の中で明らかになったのですが、Windows版 J2SE1.4.2_02 では弱参照が仕様通りに動かないことが明らかになりました。 スレッドの結末として、バグ報告が行われました。
JavaRanch に足を運んでみると、CPU 利用率が高すぎる問題の調査を助けてほしいというリクエストがありました。 アプリケーションが CPU を使いすぎる理由はいくつかあります。 まず、たくさんの異なるものが CPU を使用している、という事実を理解しなくてはいけません。 機械の上で実行されている多くのアプリケーションに加えて、OS そのものも CPU クロックを消費しているのです。 問題が OS にあるのか、それとも実行中のアプリケーションのどれかにあるのかを調べるには、CPU 利用率を監視するのが一番良い方法です。
一般的に、OS の CPU 利用率は非常に低いものです。 これが CPU 全体の利用率のかなりの部分を占めている場合、アプリケーションのどれか(あるいはその組み合わせ)が OS に負荷をかけているかどうかを確かめる必要があります。 OS はグローバルに使用可能なリソースに対する保護を提供するために、これらリソースを OS のサービスに見せかけ、アプリケーションが必ず OS を通じてアクセスするよう強制します。 その過程で OS は仕事をする必要があります。 このサービスには、ファイルシステムアクセスやスケジューリングなどが含まれます。
CPU 過負荷のもう一つの、そして最もわかりやすい原因はアプリケーション自身です。 この場合の最も良い対処は、ボトルネックを特定するためにアプリケーションをプロファイリングすることです。 議論の中では商用製品である OptimizeIt への言及がありました。 代わりになる無償のプロファイラとしては、単に VM 自身が生成する hprof をダンプする、という手があります。 ボトルネックが特定されれば、問題を分析して解決するのも簡単になります。
Javva The Hutt は家庭のイベントで多忙のため、原稿を書く時間を取れませんでした。 読者のみなさまにお詫びを申し上げる、とのことです。
じゃ、またな
Javva The Hutt.
今月のインタビューは Jive software 創業者の Matt Tucker と Bill Lynch です。 Jive の名にあまりなじみが無いとしたら、そのこと自体が彼らの製品が如何にうまく働いているかを示しています。 その製品は、ウェブ上で人気の有る数多くのディスカッショングループ (Sun や Javalobby のものなど) の背後で静かに動作しているのです。 Bill と Matt は大きな動的コンテンツを持つ利用者の非常に多いインタラクティブなサイトのスケーラビリティを 如何に保つかについて詳細に語ってくれました。 ボータルを構築している読者にとって最も役に立つパフォーマンスティップス集となることでしょう。
JPT: 自己紹介をお願いします。
私達は Jive Software の創業者で、技術面でのリーダでもあります。 2人とも何足ものわらじを履いていますが、最も多くの時間は Jive Software の主力製品である Jive Forums の仕事に費やしています。
JPT: 最近になって気づいたのですが、Sun のディスカッションフォーラムは Jive Forums を使っていますね。 Sun があなた方の最大の顧客なのですか? それとも、更に大きな Jive Forums を使ったサイトが有るのでしょうか?
Sun は我々の最初の大口顧客でした。 彼らとの関係は本当にプロフェッショナルなもので、そのおかげで Jive Software を3年前に始めることができたのです。 彼らのフォーラムは確かに大きなものですが、実際のトラフィックは我々の顧客の中では中くらいです。 たとえば、コミュニティ中心の Babycenter や StarWars.com などはもっと多くの投稿とページビューが有ります。 「巨大な」フォーラムになると、1日に100万ページビュー以上になりますし、データベース内のメッセージはいつでも数百万件に上ります。 この種のトラフィックに対するスケーラビリティの源は巨大な差分器です。 無償の PHP フォーラムパッケージには真似できません。 というのは、適切なキャッシュの仕組みを備えていないからです。
JPT: あなた方はこれまでに興味深いスケーラビリティの問題を解決してきていると思うのですが、いくつか紹介していただけますか?
1日に何百万回もの変更が加えられるような動的ページを間違いなく提供するためには、スケーラビリティに関するたくさんの挑戦が必要です。 おおまかに言えば、スケーラビリティはキャッシュアーキテクチャに因ります。 幸い、ディスカッションフォーラムの場合はキャッシュがうまく働きます。 というのはユーザは普通メッセージを投稿するより読むことの方が多いからです。 さて、キャッシュは基本的に以下の2つの形態のどちらかを取ります。
1) システム中のオブジェクトのキャッシュ -- フォーラム、カテゴリ、メッセージ、スレッド、ユーザ。 この種のキャッシュの特徴は、長く残ることとサイズに限りがあることです。 我々はそれぞれのオブジェクトがメモリ内でどれだけの大きさになるか計算し、4MB をこのキャッシュ、32MB を別のキャッシュ、という風に割り当てられるようにしています。 オブジェクトは LFU アルゴリズム (最も使用頻度の低いものを選ぶ) に従ってキャッシュから押し出されます。-- それぞれ 50K のメッセージを数百万扱うのであれば、それらを全部メモリ内に詰め込む方法は無いのです。 とはいえ、LFU はこのケースでは完璧です。 なぜなら、最新のメッセージは繰り返し読まれる一方で古いメッセージは滅多にアクセスされないものだからです。
2) オブジェクト間の関係のキャッシュ。スレッド内のメッセージ一覧、更新日でソートされたフォーラム内のスレッド一覧など。
最初のタイプのキャッシュはとても単純です。 問題は 2番目のタイプのキャッシュであり、挑戦しがいが有るのはこちらです。 一例を挙げましょう。非常にたくさんのトラフィックが有るサイトでは 1秒間に複数の新しいメッセージが投稿されます。 フォーラム中のメッセージの一覧をキャッシュしようとすると、そのリストは 1秒の内に何度も (データベースの検索が追いつかないほど速く) キャッシュを出たり入ったりすることになるので、データベースの限界にすぐに突き当たります。 この問題を解決するために、特別なキャッシュモードを用意して特定のアイテムを最低 N 秒間(通常 5 または 10秒間) キャッシュ内に留めるようにしています。 このことにより、トラフィックが連続的に増加するときのキャッシュあふれの回数に上限をかけることができます。 それから、キャッシュ更新用のスレッドを分けることで、新しいデータがデータベースから読み出されてキャッシュに納まるまではユーザがちょっと古めのデータを読み続けられるようにできます。 基本的にユーザにとっては、データベースへの打撃のために検索が全く追い付かずにあらゆるコンテンツが読めない状態になるよりはこの 5秒 10秒に投稿されたメッセージを読めないことの方が、ずっとましです。
我々が手がけた別の面白い事例はクラスタリングです。 我々がサポートするアプリケーションサーバは Tomcat や Resin といったものなので、EJB を使うことはありません。 そのため、我々は Tangosol 社の Coherence という製品を使って自分達の手でクラスタリングを実現しなければなりませんでした。 最も単純に使うなら Coherence はクラスタ化された Map の実装であり、我々はその上にキャッシュ構造を作り上げています。 Coherence は高度なキャッシュモードに対応できる複雑さも備えており、他にも呼び出しサービス (Runnable を渡すとすべてのクラスタメンバ上で実行して結果を返してくれる) のような有用なクラスタリングサービスも提供してくれます。
おお。大成功した製品のスケーラビリティの秘密を惜し気もなく教えてくださったように思います。 ありがとう。 読者のみなさんは、特に同じページを何度も表示しなければならないポータルで性能問題を抱えているような方は、喜んでいることでしょう。 さらに図々しく質問してみます。 性能上の注意点はもっと他にも有りますか?
はい。スケーラビリティに関する興味深い事例がいくつか有ります。
1) 製品をほぼデータベース非依存にし、MySQL から Oracle、DB2 まで何でもサポートしながらもデータベースに関して良好なパフォーマンスを実現しています。
2) 使用する JSP タグライブラリの数を厳密に制限しています。 ほとんどのアプリケーションサーバにとっては動的なページの閲覧数が非常に多くなるとタグライブラリのオーバヘッドが非常に大きくなるためです。
素晴らしいティップスをありがとう。 どうやってデータベース非依存のまま高性能を達成したのかというところに話を進めてもらっても良いですか? それともそれは企業秘密でしょうか?
性能を上げつつも多様なデータベースでうまく動作するコードベースを維持するための戦略を幾つか持っています。 私達はコネクションを取得するために ConnectionManager というクラスを使っています。 このクラスは、データベースに関するたくさんのメタデータを保持します。 メタデータには JDBC ドライバにより報告されるものや我々が直にコード化したものが有ります。 たとえば以下のようなメタデータフィールドが有ります。
// データベースがトランザクションをサポートしていたら True
private static boolean transactionsSupported;
// データベースが Statement.setMaxRows() メソッドをサポートしていたら True
private static boolean maxRowsSupported;
// データベースが Statement.setFetchSize() メソッドをサポートしていたら True
private static boolean fetchSizeSupported;
// データベースが相関サブクエリをサポートしていたら True
private static boolean subqueriesSupported;
// データベースがスクロールインセンシティブな結果をサポートしていたら True
private static boolean scrollResultsSupported;
// データベースがバッチ更新をサポートしていたら True
private static boolean batchUpdatesSupported;
これにより、我々はサブクエリをサポートするデータベースに対する手段とサポートしない MySQL に対する別の手段の両方を用意できます。 使用されているのが特定のどのデータベースかを知りたい場面も色々有ります。
/**
* Jive が接続するデータベースの種類を識別するクラス。
* ほとんどの場合ではデータベース固有の機能を必要としないし、
* 使用中のデータベースの種類を知ることも必要ない。
* しかし、性能上の理由でデータベースの種類を知ることがどうしても
* 必要となる場合も確かに有る。
*/
public static class DatabaseType {
public static final DatabaseType ORACLE = new DatabaseType();
public static final DatabaseType POSTGRES = new DatabaseType();
public static final DatabaseType MYSQL = new DatabaseType();
public static final DatabaseType OTHER = new DatabaseType();
//
private DatabaseType() {
/* 何もしない */
}
}
この情報は、たとえば Oracle にヒントを与えるといったデータベース依存の最適化を施すような決定的な場合で使用します。 このように戦略を組み合わせることにより、データベースに関するコードの 95%を汎用的なものにし、 一方で必要な部分には最適化を施せるようにしておけるのです。
どうもありがとう! ほんとうに役に立つ情報です。 多くの読者がこのインタビューを役立ててくれるだろうと思います。 あなたがたの製品の成功にお祝いを申し上げます。 お話を聞いた後ではより一層印象深い物に思えます。
The JavaPerformanceTuning.com team
Java プログラムは理論的には C プログラムより速くなることもあるとどこかで読みました。 そんなことって有るんですか?
どちらのプログラムも最終的には特定の CPU 上の機械語として実行されるので、理論的には片方からもう一方へと リバースエンジニアリングを施すことができます。 したがって、どちらのプログラムからも同じ性能を得られるでしょう。 もっと現実的に考えると、Java は実行時にプログラムを最適化できるバーチャルマシンを持つが C の方は静的にコンパイルされたプログラムである、という話をされているのでしょう。 そのことを踏まえて、Java バーチャルマシン(JVM) は静的オプティマイザには使えない実行時情報を使えるので理論的にはより効果的にプログラムを最適化できると言う人もいます。 これを示す具体的な例は有るのでしょうか?
有ります。Jeff Kesselman(Sun のゲーム担当のエンジニア) の java.net 上のブログ (see the blog here) に書かれている興味深い議論の中で、以下の点が指摘されてます。
Java コード (に限らず実行時にコンパイルされるコード) は事前にコンパイルされているコードに比べてより速く実行できる可能性を持っています。
例:
一般的には、現在の Java はスピードの面では C++ と同等です。 C でやった方が常に速い、といった種類の低レベルの処理が有ります。 そういう処理の一つに巨大なバイト配列に対するアクセスがあります。 典型的な例は MPEG ストリームのデコードです。 JMF に JNI ベースのコーデックを付属させているのはそれが理由です。 (この極端な場合においても、15% 程度の高速化を得られるのみです。)
Java が常に C を打ち負かすような種類の処理も有ります。 Java のメモリ割り当てと解放は C と比べると超高速です。 ループや再帰に対するより高度な最適化のおかげで FFT の処理は C による最良のものをも常に上回ります。
さて、口先だけの理屈屋が束になって必死に、自分なら C プログラムを Java バージョンよりも速く最適化できると書いて寄こす前に、ここまでの論点をはっきりさせておきましょう。 ものによっては Java の方が速く、また別のものによっては C の方が速くなります。 特定の実行環境の条件をよく知っているなら C プログラムをとことん最適化することも可能でしょう。 その場合多くの時間が、割り当てと解放、ループ、再帰呼び出しを Java 同様に高速にするために既存のコードを解析することに費やされることは疑い有りません。 私の方は Java で同じことができます。 その場合は手作業でのコードの最適化とオブジェクトの数を減らすことに多くの時間を費やすことになるでしょう。 最終的には双方とも、可能な限り高速に動作するアセンブラライクなバージョンに到達するまで続けることができます。
しかし、大多数のプログラムには最適化にすべてを費やせるような人的資源は割り当てられていません。 あなたには人生というものは無いのかもしれませんが、私にはもっと大事なことが他にも有るのです。 大抵のプログラムはコンパイラや実行時の最適化から恩恵を受けられます。その上で、十分な速度が得られるまで残りのボトルネックを取り除くよう手動の最適化を頑張れば良いでしょう。
そういう環境においては、JVM による実行時最適化は C プログラムで可能な静的な最適化よりうまくやれます。 そう、実行環境について知り、適応できるようにすることは、ほとんどの静的にコンパイルされたプログラムではできないような種類の最適化を可能にします。 その静的にコンパイルされたプログラムがターゲットとする実行環境についての情報が非常に詳細に与えられている場合は別ですが。
これらのことから言えるのは、もともとの質問に対する答えは:はい、そういうことも有ります。
The JavaPerformanceTuning.com team
http://www.onjava.com/pub/a/onjava/2004/03/17/lazyAspects.html
Aspect で遅延ローディング
(最終更新 2004-03, 追記 2004-05-31, 著者 Russ Miles, 出典 OnJava). Tips:
http://www-106.ibm.com/developerworks/library/j-perf03174.html
NIO でスケールアップを果たした MegaJogos
(最終更新 March 2004, 追記 2004-05-31, 著者 Jack Shirazi Kirk Pepperdine, 出典 IBM). Tips:
http://weblogs.java.net/pub/wlg/1101
J2EEパフォーマンスtips
(最終更新 2004-03, 追記 2004-05-31, 著者 Carol McDonald, 出典 java.net). Tips:
http://weblogs.java.net/pub/wlg/1092
コネクションリークの穴をふさぐ
(最終更新 2004-3, 追記 2004-05-31, 著者 Bob Lee, 出典 java.net). Tips:
http://wiki.java.net/bin/view/Javapedia/AlwaysUseStringBufferMisconception
つねに StringBuffer を使うべきだという誤解について
(最終更新 2003-06, 追記 2004-05-31, 著者 dubwai, 出典 java.net). Tips:
http://java.sun.com/developer/JDCTechTips/2004/tt0122.html#2
クラスローディングとガベージコレクションの監視
(最終更新 2004-01, 追記 2004-05-31, 著者 John Zukowski, 出典 Sun). Tips:
http://wiki.java.net/bin/view/Linux/PerformanceTipsForJavaOnLinux
Linux でのパフォーマンス監視
(最終更新 2004-01, 追記 2004-05-31, 著者 JamesCLiu, 出典 java.net). Tips:
http://javaboutique.internet.com/articles/jvm_monitor/
JVM監視・管理仕様
(最終更新 2004-04, 追記 2004-05-31, 著者 Benoy Jose, 出典 javaboutique). Tips:
http://www.fawcette.com/javapro/2003_03/magazine/features/odoerderlein/
スレッディングの技でプログラムを改良する
(最終更新 2004-03, 追記 2004-05-31, 著者 Osvaldo Pinali Doederlein, 出典 JavaPro). Tips:
http://www-106.ibm.com/developerworks/websphere/techjournal/0405_brown/0405_brown.html
J2EE ベストプラクティス
(最終更新 2004-05, 追記 2004-05-31, 著者 Kyle Brown Keys Botzum Ruth Willenborg, 出典 IBM). Tips:
http://www.javaworld.com/javaworld/jw-04-2004/jw-0405-bottleneck.html
J2EE データ層のボトルネック
(最終更新 2004-04, 追記 2004-05-31, 著者 Christopher Keene, 出典 JavaWorld). Tips: