純規の暇人趣味ブログ

広くて浅い知識の湖

[Java]JDK付属のJVisualVMでプロファイリング

      HimaJyun

プログラムに隠れた無駄な処理を省き、真のパフォーマンスを叩き出すためには欠かせない「最適化」

そのためにはプログラム内のどこで時間を食ってるかを見つけ出す必要があります。

今回はJava(JDK)に付属しているプロファイラ「JVisualJM」を利用して、Javaアプリケーションのプロファイリングをば……

JVisualVM

本文に入る前に、突然ですけど、パレートの法則ってご存知?「2:8の法則」とかって言えば大半の人は理解出来るかな?

Wikipediaの解説の例にあるように、実際、多くのプログラムで行われている処理の8割が2割のコードによって実行されています(すなわち、負荷の8割が2割に集中している)

そこを見つけ出して叩き潰せばかなりの高速化が達成出来るわけです。(1回あたり0.1秒の節約でも10回呼び出せばはや1秒の節約になる)

行き過ぎた最適化はNG

早い事は良い事ですが、行き過ぎた最適化でメンテナンス性の悪化やバグを生んでしまっては本末転倒です。

最近だとJITコンパイラも優秀ですし、ガッチガチに手動最適化してコンパイラと同等、とか、アセンブリで書いてコンパイラと同等、なんてのは良くある事です。(下手な手動より優れた自動、とでも呼びましょうか)

少なくとも、10回のforを手で展開するなんてのは以ての外ですよ。(そんなのは人がやろうがやらまいがコンパイラがやりますから)

プログラマがやるべきは根本の所、アルゴリズムの改善でしょう。(例えばバブルソートをクイックソートに変えるとか、データを事前にディスクから読み込んでおくとか……コンパイラの手が届かない範囲)

OpenJDKの場合

OpenJDKだとJVisualVMが付属していない(?)みたいです。(少なくともUbuntu 16.04でaptからopenjdkをインストールすると付いてなかった)

その場合は、別途「VisualVM」をインストールします。(J(Java)が付いていないのは商標の問題かな?)

aptの場合は以下の通り。

sudo apt-get install visualvm

どこにある?

JDKがインストールされたディレクトリの中の「bin」にjvisualvm.exeがあります(他のOSだとどうなるかは知らん)

exeと言う銘を打ってあるだけで、中身はしっかりJavaなので、実行にはもちろんJDKが必要です。(JRE不可)

先程aptでインストールした J VisualVMの場合はターミナルなどから「visualvm」コマンドで実行できるはず……

サンプリング

とりあえず先に測定したいアプリケーションを起動しておきましょう。(後から起動しても認識する、まぁどちらでも良い)

今回は適当にEclipseで……(VisualVMでVisualVM自身をプロファイリングすることも可能)
java-jvisualvm-001

ダブルクリックで開けるので開く。
java-jvisualvm-002

グラフとかスレッドとかは適当に眺めていたら分かるので端折るとして、まずはプロファイリング

CPUのサンプリング

「サンプラ」と「プロファイラ」があって、似たような画面になっているけど、サンプラの方を使用。(プロファイラは何故か動かなかった、こっちの方が重要そうなのに……)

サンプラ画面を開いて、「CPU」をクリック!!
java-jvisualvm-003

サンプリングが始まるので、適当に操作しよう。
java-jvisualvm-004

程よく集まって来たっぽければ、「ストップ」をクリックしてサンプリングを停止させる(そうしないとリアルタイムで色々情報が入って来るので見づらい)
java-jvisualvm-005ある瞬間の情報が欲しいなら「スナップショット」をクリックでもOK

グラフの見方……の説明は要らないかな、見たまんまなので。

ちなみに上の画像で処理の大半を占めているのは単なるWinのイベントループ。

メモリのサンプリング

先程のサンプリング画面で「CPU」の横にあった「メモリー」を押すと、メモリのサンプリングが取得可能。
java-jvisualvm-006

異常にメモリを食いまくる怪しいクラス(メモリリークの疑い)があれば役に立つ。

取得する情報を絞りたい

例えば、先程の例であればWinのメッセージループがCPU時間の大半を占めていた。

ただ、マ族(プログラマの事を勝手にそう呼んでいる)の者達はそんな事イチイチ言われなくても分かっている。

情報はパッケージ単位で絞り込むことが可能、「設定」にチェックを入れて、出てきた画面にパッケージ名を入れるだけ。
java-jvisualvm-007

パッケージのみプロファイルだとか、パッケージをプロファイルしないだとか、回りくどい(冗長な)表現になっている所にじゃばみを感じる。

「パッケージのみプロファイル」は指定したパッケージで呼び出された物だけを取得する。

「パッケージをプロファイルしない」は指定したパッケージで呼び出された物を除外する。

これを指定して実行すれば情報を絞り込む事が可能。

プロファイラ

「プロファイラが動かない」と言ったけど「動かない様に見えていた」だけだった。

何気にさっき説明した方法でサンプリング対象をとてもとてもとてもとても小さい範囲に絞り込むと動いた。

どうやら、そのまま(全体が対象)では実行時のオーバーヘッドが大き過ぎて時間が掛かっているみたい。(サンプリングの時より複雑な細工を施しているのかな?)

とは言え、Eclipseだと絞り込んでも動かなくなるので、ここでは比較的小さそうなMinecraftのLauncher(ゲーム本体ではなくLauncherだけ)で実行してみる。
java-jvisualvm-008

基本的な見方、使い方はサンプリングの時と同じ。(むしろ何が違うのかが分からない)

プロファイラの方を使いたい時は絞り込まないとまともに動かないかも知れない(でもそれってプロファイラの意味がない気が)

ヒープダンプ

もう一つ面白い機能が

「監視」タブの中にある「ヒープダンプ」を選択すればヒープ領域のダンプが取得出来る。
java-jvisualvm-009

これを使えば、その瞬間のヒープをまるっとじっくり解析できる。

OQLコンソール

良く分からないけど面白いのがこの「OQLコンソール」

java-jvisualvm-010

右下にあるいくつかのサンプル、これらを開いて実行すると……
java-jvisualvm-011

問い合わせ結果に一致するオブジェクトの一覧が取得できる。
java-jvisualvm-012

最後に

と言った具合に、良く分からないけど最低限の解析くらいは出来そう。

プロファイラがやたらと重いのを除けば悪くはない。

とは言え、プロファイラを使ってまで性能を出さないと行けない様な案件、もしくは、プロファイラで調べないと行けないくらいに重たいプロジェクトなんてのはそうそうないと思うけど……

 - プログラミング