Javaアプリケーション内からのCPU使用率のプロファイリング
2002年11月8日
Q: JavaのCPU使用率をどのように判断しますか?
A:では、ここに良いニュースと悪いニュースがあります。悪いニュースは、純粋なJavaを使用してCPU使用率をプログラムで照会することは不可能であるということです。このためのAPIはありません。提案された代替手段はRuntime.exec()
、JVMのプロセスID(PID)を決定し、のような外部のプラットフォーム固有のコマンドを呼び出し、ps
対象のPIDの出力を解析するために使用できます。しかし、このアプローチはせいぜい壊れやすいものです。
ただし、幸いなことに、Javaの外に出て、Java Native Interface(JNI)を介してJavaアプリケーションと統合するCコード行をいくつか記述することで、信頼性の高いソリューションを実現できます。Win32プラットフォーム用の単純なJNIライブラリを作成することで、それがいかに簡単であるかを以下に示します。[リソース]セクションには、独自のニーズに合わせてカスタマイズしたり、他のプラットフォームに移植したりできるライブラリへのリンクが含まれています。
一般に、JNIの使用はやや複雑です。ただし、Javaからネイティブコードへの一方向のみを呼び出し、プリミティブデータ型を使用して通信する場合、状況は単純なままです。JNIには多くの優れたリファレンス(「参考文献」を参照)があるため、ここではJNIチュートリアルを提供しません。実装手順の概要を説明するだけです。
まずcom.vladium.utils.SystemInformation
、ネイティブメソッドを宣言するクラスを作成します。このクラスは、現在のプロセスでこれまでに使用されたCPU時間のミリ秒数を返します。
public static native long getProcessCPUTime();
JDKのjavahツールを使用して、将来のネイティブ実装用に次のCヘッダーを生成します。
JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime(JNIEnv * env、jclass cls)
ほとんどのWin32プラットフォームでは、このメソッドはGetProcessTimes()
システムコールを使用して実装でき、文字通り3行のCコードです。
JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime(JNIEnv * env、jclass cls){FILETIME CreationTime、exitTime、kernelTime、userTime; GetProcessTimes(s_currentProcess、&creationTime、&exitTime、&kernelTime、&userTime); return(jlong)((fileTimeToInt64(&kernelTime)+ fileTimeToInt64(&userTime))/(s_numberOfProcessors * 10000)); }
このメソッドは、現在のプロセスに代わってカーネルとユーザーコードの実行に費やされたCPU時間を追加し、プロセッサの数で正規化し、結果をミリ秒に変換します。fileTimeToInt64()
変換ヘルパー関数であるFILETIME
64ビット整数に構造をし、s_currentProcess
そしてs_numberOfProcessors
便利なJVMは、ネイティブライブラリをロードしたときに一度と呼ばれていますJNIメソッドで初期化することができるグローバル変数です。
静的ハンドルs_currentProcess; static int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm、void * reserved){SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess(); GetSystemInfo(&systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; JNI_VERSION_1_2を返します。}
getProcessCPUTime()
Unixプラットフォームに実装する場合は、getrusage
システムコールを開始点として使用する可能性が高いことに注意してください。
Javaに戻り、ネイティブライブラリ(silib.dll
Win32上)をロードするには、SystemInformation
クラスの静的初期化子を使用するのが最適です。
プライベート静的最終文字列SILIB = "silib"; static {try {System.loadLibrary(SILIB); } catch(UnsatisfiedLinkError e){System.out.println( "native lib '" + SILIB + "' not found in'java.library.path ':" + System.getProperty( "java.library.path")); eを投げる; //再スロー}}
getProcessCPUTime()
JVMプロセスの作成以降に使用されたCPU時間を返すことに注意してください。このデータ自体は、プロファイリングには特に役立ちません。さまざまな時点でデータスナップショットを記録し、任意の2つの時点間のCPU使用率を報告するには、より多くのユーティリティJavaメソッドが必要です。
public static final class CPUUsageSnapshot {private CPUUsageSnapshot(long time、long CPUTime){m_time = time; m_CPUTime = CPUTime; } public final long m_time、m_CPUTime; } //ネストされたクラスの終了publicstatic CPUUsageSnapshot makeCPUUsageSnapshot(){return new CPUUsageSnapshot(System.currentTimeMillis()、getProcessCPUTime()); } public static double getProcessCPUUsage(CPUUsageSnapshot start、CPUUsageSnapshot end){return((double)(end.m_CPUTime --start.m_CPUTime))/(end.m_time --start.m_time); }
「CPUモニターAPI」の準備がほぼ整いました!最後の仕上げとして、シングルトンスレッドクラスを作成しますCPUUsageThread
。これは、定期的な間隔(デフォルトでは0.5秒)でデータスナップショットを自動的に取得し、CPU使用率イベントリスナーのセット(おなじみのオブザーバーパターン)に報告します。このCPUmon
クラスは、CPU使用率をSystem.out
次のように出力するだけのデモリスナーです。
public static void main(String [] args)throws Exception {if(args.length == 0)throw new IllegalArgumentException( "usage:CPUmon"); CPUUsageThreadモニター= CPUUsageThread.getCPUThreadUsageThread(); CPUmon _this = new CPUmon(); クラスapp = Class.forName(args [0]); メソッドappmain = app.getMethod( "main"、new Class [] {String []。class}); String [] appargs = new String [args.length-1]; System.arraycopy(args、1、appargs、0、appargs.length); monitor.addUsageEventListener(_this); monitor.start(); appmain.invoke(null、新しいオブジェクト[] {appargs}); }
さらに、元のアプリケーションを起動する前CPUmon.main()
に開始CPUUsageThread
することを唯一の目的として、別のJavaメインクラスを「ラップ」します。
デモとして、CPUmon
JDK1.3.1のSwingSet2Swingデモを実行しました(OS環境変数またはJavaプロパティのsilib.dll
いずれかでカバーされる場所にインストールすることを忘れないでください)。PATH
java.library.path
> java -Djava.library.path =。 -cp silib.jar;(my JDK install dir)\ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID:339] CPU使用率:46.8%[PID:339] CPU使用率:51.4%[PID:339] CPU使用率:54.8%(ロード中、デモは私のマシンの2つのCPUのうちの1つをほぼ100%使用します)... [PID:339] CPU使用率:46.8%[PID:339] CPU使用率:0%[PID: 339] CPU使用率:0%(デモはすべてのパネルのロードを終了し、ほとんどアイドル状態です)... [PID:339] CPU使用率:100%[PID:339] CPU使用率:98.4%[PID:339] CPU使用率:97%(両方のCPUを使用するCPU集中型アニメーションを実行するColorChooserDemoパネルに切り替えました)... [PID:339] CPU使用率:81.4%[PID:339] CPU使用率:50%[PID :339] CPU使用率:50%(Windows NT Task Managerを使用して、単一のCPUを使用するように「java」プロセスのCPUアフィニティを調整しました)..。
もちろん、タスクマネージャーを介して同じ使用数を監視することはできますが、ここでのポイントは、同じデータをプログラムで記録する方法があるということです。長時間実行されるテストやサーバーアプリケーションの診断に役立ちます。完全なライブラリ(「リソース」で入手可能)には、プロセスPIDを取得するためのメソッド(外部ツールとの統合用)など、他のいくつかの便利なネイティブメソッドが追加されています。
Vladimir Roubtsovは、1995年以来Javaを含め、12年以上にわたってさまざまな言語でプログラミングを行ってきました。現在、テキサス州オースティンでTrilogyのシニア開発者としてエンタープライズソフトウェアを開発しています。楽しみのためにコーディングするとき、ウラジミールはJavaバイトコードまたはソースコードインストルメンテーションに基づいたソフトウェアツールを開発します。このトピックの詳細
- この記事に付属する完全なライブラリをダウンロードしてください
//images.techhive.com/downloads/idge/imported/article/jvw/2002/11/01-qa-1108-cpu.zip
- JNI仕様とチュートリアル
//java.sun.com/j2se/1.4/docs/guide/jni/index.html
- JNIの概要については、Stuart Dabbs HallowayのJavaプラットフォーム用コンポーネント開発(Addison-Wesley、2001年12月、ISBN0201753065)を参照してください。
//www.amazon.com/exec/obidos/ASIN/0201753065/javaworld
- 「JavaTip92Use the JVM Profiler Interface for Accurate Timing」では、JesperGortzがCPU使用率をプロファイリングするための別の方向性を探っています。(ただし、JVMPIを使用すると、この記事のソリューションと比較して、プロセス全体のCPU使用率を計算するためにより多くの作業が必要になります)
//www.javaworld.com/javaworld/javatips/jw-javatip92.html
- 完全なQ&Aカタログについては、Java Q&Aインデックスページを参照してください。
//www.javaworld.com/columns/jw-qna-index.shtml
- 100を超える洞察に満ちたJavaのヒントについては、JavaWorldのJavaのヒントのインデックスページにアクセスしてください。
//www.javaworld.com/columns/jw-tips-index.shtml
- JavaWorldのトピックインデックスのコアJavaセクションを参照する
//www.javaworld.com/channel_content/jw-core-index.shtml
- Javaビギナーディスカッションでより多くの質問に答えてください
//forums.devworld.com/[email protected]@.ee6b804
- JavaWorldの無料の毎週の電子メールニュースレターにサインアップしてください
//www.javaworld.com/subscribe
- .netの姉妹出版物からIT関連の記事が豊富にあります。
このストーリー「Javaアプリケーション内からのCPU使用率のプロファイリング」は、もともとJavaWorldによって公開されました。