Google Chartでヒープグラフ

従来ヒープ情報のようなリアルタイム更新するグラフを実装する場合、

  • サーバサイドで画像を生成し、ブラウザへ送る
  • サーバからデータを受け取って、ブラウザがアプレットでグラフを描画する

のどちらかだったと思います*1
前者はサーバに負荷がかかるため、後者が一般的でしょう。けどアプレットって起動が結構重いので、イライラしますね。
そこへ第3の選択肢が現れました!Google ChartはサーバでもクライアントでもなくGoogleインフラが描画をやっちゃってくれます。早速作ってみました。

index.html

<html>
<head>
	<meta http-equiv="content-type" content="text/html;charset=Shift_JIS">
	<title>Simple Console</title>
	<script type="text/javascript" src="jquery-1.2.3.js"></script>
	<script>
		var NUM_OF_SAMPLES = 60; // 表示サンプル数
		var timerId;
		var maxMemory = 0;
		var maxChd;
		var totalChd;
		var usedChd;

		function init() {
			// 同期でmaxMemoryを取得
			jQuery.ajax({
				async: false,
				url: "resource.jsp",
				data: "cmd=maxMemory",
				success: function(data){maxMemory = data}
			});
			// 初期データ文字列を組み立て
			maxChd = "100";
			totalChd = "0";
			usedChd = "0";
			for (i = 1; i < NUM_OF_SAMPLES; i++) {
				maxChd += ",100";
				totalChd += ",0";
				usedChd += ",0";
			}
			// 周期呼び出し
			timerId = setInterval("loadChart()", $("#updatePeriod").attr("value") * 1000);
		}

		function loadChart() {
			// 非同期でヒープ情報を取得
			jQuery.get("resource.jsp", "cmd=heap",
				function(dataStr){buildChart(dataStr)});
		}

		function buildChart(dataStr) {
			var data = dataStr.split(",");
			// maxMemoryに対する比を0〜100で表現
			totalChd += "," + Math.round(data[0] * 1000 / maxMemory) / 10; 
			usedChd += "," + Math.round(data[1] * 1000 / maxMemory) / 10;
			// データ文字列をずらす
			totalChd = totalChd.substring(totalChd.indexOf(",") + 1);
			usedChd = usedChd.substring(usedChd.indexOf(",") + 1);
			// URL組み立て
			var url = "http://chart.apis.google.com/chart?" + 
				"chs=480x320&cht=lc&chxt=x,y&chco=ff0000,00ff00,0000ff&chdl=Max|Total|Used&" +
				"chxl=0:||1:||" + Math.round(maxMemory / (1024 * 1024)) + "MB&" +
				"chd=t:" + maxChd + "|" + totalChd + "|" + usedChd;
			// srcを差し替える
			$("#heapChart").attr("src", url);
			// URLをデバッグ出力
			$("#debug").html(url);
		}

		function changePeriod() {
			clearInterval(timerId);
			timerId = setInterval("loadChart()", $("#updatePeriod").attr("value") * 1000);
		}
		
		function gc() {
			// 非同期でGCを実行
			jQuery.get("resource.jsp", "cmd=gc");
		}
	</script>
</head>
<body onload="init()">
	<img id="heapChart" src="" alt="heap chart">
	<br>
	<form name="form1">
		更新周期<input type="text" id="updatePeriod" size="5" value="1">秒
		<input type="button" value="周期変更" onclick="changePeriod()">
		<br>
		<input type="button" value="GC" onclick="gc()">
		<br><br>
		<div id="debug" style="width:480px;word-wrap:break-word;word-break:break-all;"></div>
		<br>
	</form>
</body>
</html>

resource.jsp

<%@page session="false" contentType="text/html; charset=Shift_JIS" %>
<%@page import="java.util.*" %>
<%
response.setHeader("Expires", "-1");
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
String cmd = request.getParameter("cmd");
if (cmd.equals("maxMemory")) {
	Runtime runtime = Runtime.getRuntime();
	out.print(
		runtime.maxMemory()
	);
	
} else if (cmd.equals("heap")) {
	Runtime runtime = Runtime.getRuntime();
	out.print(
		runtime.totalMemory() + "," +
		(runtime.totalMemory() - runtime.freeMemory())
	);
} else if (cmd.equals("gc")) {
	System.gc();
}
%>

resource.jspは単純にヒープ情報のデータだけ返します。index.htmlは非同期でjspを呼び出し、Google ChartのURLを組み立て、imgタグのsrc属性を差し替えます。1秒間隔の更新でも全く問題ありません。さすがGoogle!
JavaScriptはまだまだビギナーなのでもっさいコードがあるかもしれません。jQueryを使ってみました。コードが少しはすっきりしたかな?

http://toshiyakobayashi.googlepages.com/scon.zip
からダウンロードできるようにしました。解凍し、Tomcatならwebapps/Rootの下に、JBossならdeploy/jboss-web.deployer/ROOT.warの下にsconディレクトリをコピーしたらOKです。

*1:どちらにしてもライブラリはJFreeChartあたり