top / index / prev / next / target / source

2003-01-14 diary: [Java] Javaパフォーマンス調査

いがぴょんの日記 日記形式でつづる いがぴょんコラム ウェブページです。

old-v2

[Java] Javaパフォーマンス調査

仕事でJavaパフォーマンスの調査に携わっていて… +超単純メモリトレースツールサンプル

Javaパフォーマンス調査

ここのところ ちょくちょく日記にJavaパフォーマンスネタが載っていましたが、ご想像通り今 Javaパフォーマンスの調査絡みの仕事をしています。今日は 今時の 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();
    }
}

べたべたでんなぁ。もちろん このメモリトレースが有効なまんま本番に載せて行けないのは言うまでもありません (笑)

メモリトレースツール自身がメモリのゴミを生産していますからね。この例で行くとちょうど1000バイトのメモリゴミオーバヘッドがあるのがわかりますよね。

Javaパフォーマンス調査 その後…

22:35頃 ようやっと明日のプレゼンで利用する資料が完成しました。ふう。メールで事前に送付して、今日はもう おしまい。明日の京都出張は 結構早いですから 寝坊しないようにPHSの目覚ましをセットして (笑)

Javaパフォーマンス 後日談

2003年のあいだに サイド Javaパフォーマンス屋になっていました…

世間のニュースから () 2003

登場キーワード


この日記について