top / index / prev / next / target / source
日記形式でつづる いがぴょんコラム ウェブページです。
仕事でJavaパフォーマンスの調査に携わっていて… +超単純メモリトレースツールサンプル
ここのところ ちょくちょく日記にJavaパフォーマンスネタが載っていましたが、ご想像通り今 Javaパフォーマンスの調査絡みの仕事をしています。今日は 今時の Javaパフォーマンス関連書籍を 調べてみました。
Javaパフォーマンス戦略 -高速プログラムの高速開発のためのイディオム集-http://www.pearsoned.co.jp/washo/java/wa_int82-j.html C・ラーマン、R・ガスリー 著 安藤 慶一 訳 出版元: ピアソン・エデュケーション ISBN4-89471-242-3
発売当初から私がお世話になったパフォーマンスチューニングネタ満載の本。現在読み返しても やはりこの本が一番素晴らしかったです。
[The Java Series] Javaプラットフォームパフォーマンス -コードレベルのチューニングと開発プロセスへの統合- http://www.pearsoned.co.jp/washo/java/wa_java14-j.html S・ウィルソン、J・ケセルマン 著 豊福 剛 訳 出版元: ピアソン・エデュケーション ISBN4-89471-393-4
最近(2002.07)に出たパフォーマンスチューニングネタの本。パフォーマンスチューニングについてアカデミックなアプローチを取っていて この本はこれで読んでみて満足しました。また プロファイルの方法について結構詳しく書いてあるので この点も満足しました。
原書 Java Platform Performance: Strategies and Tactics by Steve Wilson and Jeff Kesselman, ISBN 0-201-70969-4
Javaの鉄則 -エキスパートのプログラミングテクニック- http://www.pearsoned.co.jp/washo/java/wa_int85-j.html P・ハガー 著 株式会社ドキュメントシステム 訳 出版元: ピアソン・エデュケーション ISBN4-89471-258-X
昔 Java言語のことがよく分かっていないときに読んで とても勉強になった本。パフォーマンスネタも扱っていて 私にとってのパフォーマンスチューニングの原点がこの本です。
この3冊を読むと、かなりパフォーマンスチューニング技法が身に付くように思っています。どれか一冊、と言われたらやはり
が最もお勧めですね。
で、これらに載っていない私の独自トレース方法をメモで書いておきます。定量的な単体テスト時のメモリトレースの方法です。3年くらい前から使っている私の愛用ツールです。ソースを見てすぐにわかるでしょうが、この方法は 特定JVMの特定HotSpotでしか同一の結果が得られないという かなり偏屈なメモリトレース方法です。でも私にとってはこのように定量的にメモリの増減を把握できるというのはとても効果絶大なのです。
で、まずはそれメモリトレースツールの実行結果 Test.classの実行結果 D:\temp\test2>java Test Memory: ******************** Memory: init begin. Memory: ******************** Memory: 294432/2031616 byte (+293432 byte) (14%) Memory: 295760/2031616 byte (+328 byte) (14%) Memory: 296768/2031616 byte (+8 byte) (14%) Memory: 297768/2031616 byte (0 byte) (14%) Memory: 298768/2031616 byte (0 byte) (14%) Memory: 299768/2031616 byte (0 byte) (14%) Memory: 300768/2031616 byte (0 byte) (14%) Memory: 301768/2031616 byte (0 byte) (14%) Memory: 302768/2031616 byte (0 byte) (14%) Memory: 303768/2031616 byte (0 byte) (14%) Memory: gc Memory: gc Memory: gc Memory: gc Memory: 157672/2031616 byte (-147096 byte) (7%) Memory: gc Memory: 157824/2031616 byte (-848 byte) (7%) Memory: 158832/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157976/2031616 byte (-1856 byte) (7%) Memory: 158984/2031616 byte (+8 byte) (7%) Memory: gc Memory: 158128/2031616 byte (-1856 byte) (7%) Memory: 159136/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157672/2031616 byte (-2464 byte) (7%) Memory: 158680/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157672/2031616 byte (-2008 byte) (7%) Memory: 158680/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157824/2031616 byte (-1856 byte) (7%) Memory: 158832/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157976/2031616 byte (-1856 byte) (7%) Memory: 158984/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157672/2031616 byte (-2312 byte) (7%) Memory: 158680/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157824/2031616 byte (-1856 byte) (7%) Memory: 158832/2031616 byte (+8 byte) (7%) Memory: gc Memory: 157976/2031616 byte (-1856 byte) (7%) Memory: gc Memory: gc Memory: gc Memory: gc Memory: gc Memory: gc Memory: ******************** Memory: init end. show() footprint is (1000) Memory: ******************** Memory: 157840/2031616 byte (-1136 byte) (7%) Memory: 158848/2031616 byte (+8 byte) (7%) Memory: 159848/2031616 byte (0 byte) (7%) Memory: 160848/2031616 byte (0 byte) (7%) Memory: 161848/2031616 byte (0 byte) (7%)あいう123 Memory: 162880/2031616 byte (+32 byte) (8%)えおか456 Memory: 163912/2031616 byte (+32 byte) (8%)きくけ789 Memory: 165104/2031616 byte (+192 byte) (8%)こさし012 Memory: 166144/2031616 byte (+40 byte) (8%)すせそ345 Memory: 167176/2031616 byte (+32 byte) (8%)たちつ678 Memory: 168208/2031616 byte (+32 byte) (8%) Memory: 169208/2031616 byte (0 byte) (8%) Memory: gc Memory: 157744/2031616 byte (-12464 byte) (7%) D:\temp\test2>
実行結果の意味はわかりますか? 『あいう123』 というのをSystem.out.println()するのに 32バイトのゴミを出している、とかいう情報がわかるツールです。処理とメモリ増加との関連を 好きなところでサンプリングできるという、まあ良くも悪くも超簡単ツールですね。ガベージコレクションの都合などでノイズが入る可能性は当然ありますけれども、ある程度メモリの状態が安定してきた後において得られる実行結果は 結構精度の良いところを行っているように感じています。
で、そのソースコードは下記。 MemoryTrace.java
/**
* 単体テスト時のメモリトレースツール
* Copyright (C) 2000-2003 Tosiki IGA
*/
public final class MemoryTrace {
private long pastMemory = 0;
private static final Runtime rt = Runtime.getRuntime();
private static final long DIVIDE = 1;
private static final String UNIT = "byte";
/**
* Sun J2SE 1.4.1_01 Windows版では 1000でした。
*/
private static final long TRACE_FOOTPRINT = 1000;
private static final int STRBUF_SIZE = 72;
public MemoryTrace() {
init();
}
/**
* メモリ状態の表示
*/
public long show() {
long totalMemory = rt.totalMemory() / DIVIDE;
long usingMemory = totalMemory - rt.freeMemory() / DIVIDE;
StringBuffer buf = new StringBuffer(STRBUF_SIZE);
buf.append("Memory: ");
buf.append(usingMemory);
buf.append("/");
buf.append(totalMemory);
buf.append(" ");
buf.append(UNIT);
buf.append(" (");
long diff = usingMemory - pastMemory - TRACE_FOOTPRINT;
if (diff > 0) {
buf.append('+');
}
buf.append(diff);
buf.append(" ");
buf.append(UNIT);
buf.append(") (");
buf.append(usingMemory*100 / totalMemory);
buf.append("%)");
System.out.println(buf.toString());
pastMemory = usingMemory;
return diff;
}
/**
* ガベージコレクションの実行。
*/
public void gc() {
show();
gcCore();
show();
}
private void gcCore() {
System.out.println("Memory: gc");
rt.runFinalization();
rt.gc();
}
/**
* 初期化処理
*/
private void init() {
System.out.println("Memory: ********************");
System.out.println("Memory: init begin.");
System.out.println("Memory: ********************");
show();
show();
show();
show();
show();
show();
show();
show();
show();
show();
gcCore();
gcCore();
gcCore();
gcCore();
gc();
gc();
gc();
gc();
gc();
gc();
gc();
gc();
gc();
gc();
gcCore();
gcCore();
gcCore();
gcCore();
gcCore();
gcCore();
System.out.println("Memory: ********************");
System.out.print("Memory: init end. show() footprint is (");
System.out.print(TRACE_FOOTPRINT);
System.out.println(")");
System.out.println("Memory: ********************");
}
}
メモリトレースのサンプルPGは下記 Test.java
/**
* メモリトレースサンプル
*/
public class Test {
/**
* 単体テスト時以外は必ずfalseに設定。
*/
private static final boolean MEMORY_TRACE = true;
public static void main(String[] args) {
MemoryTrace memoryTrace = null;
if (MEMORY_TRACE) {
memoryTrace = new MemoryTrace();
}
if (MEMORY_TRACE) {
// show()メソッドのメモリ上オーバヘッドを計測するために実行。
memoryTrace.show();
memoryTrace.show();
memoryTrace.show();
memoryTrace.show();
memoryTrace.show();
}
String msg = "あいう123";
System.out.println(msg);
if (MEMORY_TRACE)
memoryTrace.show();
msg = "えおか456";
System.out.println(msg);
if (MEMORY_TRACE)
memoryTrace.show();
int i = 789;
String msg2 = "きくけ" + i;
System.out.println(msg2);
if (MEMORY_TRACE)
memoryTrace.show();
msg = "こさし012";
System.out.println(msg);
if (MEMORY_TRACE)
memoryTrace.show();
msg = "すせそ345";
System.out.println(msg);
if (MEMORY_TRACE)
memoryTrace.show();
msg = "たちつ678";
System.out.println(msg);
if (MEMORY_TRACE)
memoryTrace.show();
// null代入がgcに与える影響は、このコメントを除去してnull代入を
// 有効にしてみるとわかります。
//msg = null;
//msg2 = null;
if (MEMORY_TRACE)
memoryTrace.gc();
}
}
べたべたでんなぁ。もちろん このメモリトレースが有効なまんま本番に載せて行けないのは言うまでもありません (笑)
22:35頃 ようやっと明日のプレゼンで利用する資料が完成しました。ふう。メールで事前に送付して、今日はもう おしまい。明日の京都出張は 結構早いですから 寝坊しないようにPHSの目覚ましをセットして (笑)
2003年のあいだに サイド Javaパフォーマンス屋になっていました…