スクリーンショット

動作中のビューア

動作中のビューア

概要

Marimite はパフォーマンス監視ツールである。

  • ソースコード内にチェックポイントを埋め込み、チェックポイント間の区間(セクション)に費やされた経過時間を測定・記録・表示
  • Java 用
    • 組み込み用コアは J2SE 5.0 以降
    • ビューアはJava SE 6 以降
  • ビューアは LAN 内のリモートマシンから実行可能
  • ビューアは Java Web Start で実行可能
  • RDBMS のセットアップ等は不要
  • 過去の記録を統計的に圧縮
    1. 経過時間の記録が100件たまると、それを昇順にソートして、50番目の値・95番目の値・最後の値の 3つを抽出し、これを第1レベルのログとして保存、残りを破棄
    2. 第1レベルのログが200件たまると、古い100件のログの値(3種類)について平均と分散を取り、これを第2レベルの ログとして保存、元となったログを破棄
    3. 第nレベルのログが200件たまると、古い100件のログの値(6種類)について平均を取り、これを第n+1レベルの ログとして保存、元となったログを破棄
  • 過去の記録のチャート(グラフ)の時間軸を対数で表示
  • イベント通知などの機能はなく、チャートを表示するだけ

デモを実行してみる

百聞は一見にしかず。

  1. サーブレットコンテナ(JBoss 4.x 以降、GlassFish v2 ur1 以降など)をインストール、起動。
  2. 最新の marimite-testbench をダウンロード・展開、marimite-testbench.war をデプロイ。
  3. http://localhost:8080/marimite-testbench/launch.jnlp を開いてビューアを起動。

主な操作方法

チャート ウィンドウのタブをドラッグ アンド ドロップすることができる。

  • 新しいウィンドウを作る: なにもない場所にドロップ
  • 別のウィンドウに移す: 既存のチャート ウィンドウにドロップ
  • タブの順番を変える: 割り込ませたい位置のタブにドロップ

チャート上でコンテキスト メニューを開くことができる。

  • 前後セクションへの移動、ズーム、印刷、削除など

ビューアを終了したとき、ウィンドウ位置は保存される。ビューアを再び起動すると、同じウィンドウ位置が再現される。

  • 保存場所はビューアを実行したマシン上

チャートの見方

  • タブのタイトル

    セクションの名前。始点と終点の名前を " - " でつないだもの。

  • 縦軸

    セクションを通過するのに要した時間。単位はミリセカンド。

  • 横軸

    ログを記録した時間。経過時間を対数で表示しており、古いものは圧縮される。

  • Live

    最近100件の経過時間。1sの時間軸上に表示されるが、これは便宜上のものであり、現実の時刻を示すものではない。

  • Median

    ログ。経過時間の記録が100件たまったとき、それを昇順にソートして50番目の値。

  • Tail

    ログ。経過時間の記録が100件たまったとき、それを昇順にソートして95番目の値。これは体感的なターンアラウンドタイムを示す値といわれる。

  • Worst

    ログ。経過時間の記録が100件たまったとき、もっとも遅い値。

  • ログのまわりの帯

    標準偏差。

測定対象のコード

package org.kaoriha.marimite.testbench;

import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.kaoriha.marimite.LocalStopwatch;
import org.kaoriha.marimite.MarimiteConfig;
import org.kaoriha.marimite.Stopwatch;

public class TestBenchServlet extends HttpServlet {
    private Thread thread = null;
    private volatile boolean isStop = false;
    private static volatile boolean IS_INITIALIZED = false;

    @Override
    public void init() throws ServletException {
        super.init();

        System.out.println("ThreadServlet#init called");
        synchronized (TestBenchServlet.class) {
            if (IS_INITIALIZED) {
                return;
            }

            MarimiteConfig.start();
            MarimiteConfig.exportPlatformMBeanServer();

            isStop = false;
            Runnable r = new Runnable() {
                private Random rnd = new Random();

                public void run() {
                    Stopwatch.start("start");
                    LocalStopwatch lsw = new LocalStopwatch("local start");
                    while (true) {
                        if (rnd.nextBoolean()) {
                            wait1();
                        }
                        if (rnd.nextBoolean()) {
                            wait2();
                        }
                        if (isStop) {
                            System.out.println("ThreadServlet thread exit");
                            return;
                        }
                        lsw.lap("local 1");
                    }
                }

                private void wait1() {
                    wait(0, 40);
                    Stopwatch.lap("wait1 exit");
                }

                private void wait2() {
                    wait(20, 80);
                    Stopwatch.lap("wait2 exit");
                }

                private void wait(int lower, int upper) {
                    int w = lower + rnd.nextInt(upper - lower);
                    try {
                        Thread.sleep(w);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            thread = new Thread(r);
            thread.start();
            IS_INITIALIZED = true;
        }
    }

    @Override
    public void destroy() {
        System.out.println("ThreadServlet#destroy called");
        synchronized (TestBenchServlet.class) {
            if (IS_INITIALIZED) {
                isStop = true;
                thread = null;
                MarimiteConfig.stop();
                IS_INITIALIZED = false;
            }
        }
        super.destroy();
    }
}