ラベル JMX関連 の投稿を表示しています。 すべての投稿を表示
ラベル JMX関連 の投稿を表示しています。 すべての投稿を表示

2011年7月27日水曜日

JMXオブジェクトをつくってみる。その1

大規模Javaプログラムを使う場合、JMXはもう必須といっていいほどものすごく便利な管理ツールです。
日本語の解説が少なすぎて切れそうですが、そんなJMXのさわりを書いてみます。

まずはプログラム
package com.ttProject.jmxtest;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class CuiEntry {
  private MBeanServer mbs;
  public static void main(String[] args) throws Exception {
    new CuiEntry();
  }
  public CuiEntry() throws Exception {
    mbs = ManagementFactory.getPlatformMBeanServer();
    mbs.registerMBean(new Test(), new ObjectName("com.ttProject.jmxtest:name=hoge"));
    System.out.println("start");
    while(true) {
      Thread.sleep(100);
    }
  }
  public interface TestMBean {
    public String getData();
    public String printData();
    public void doOutput();
  }
  public class Test implements TestMBean {
    private String data = "test";
    @Override
    public String getData() {
      return data;
    }
    @Override
    public String printData() {
      return data;
    }
    @Override
    public void doOutput() {
      System.out.println(data);
    }
  }
}
別にクラスをわけてもかまわないのですが、適当な名前のクラスとその名前にMBeanをつけたInterfaceを作成、それをregisterMBeanで登録してやると、JconsoleとかでJMX経由でサーバーの状態を参照したり、特定のプログラムを実行したりできます。

自分のオリジナルのクラスが追加されました。

2011年7月24日日曜日

JMX接続を自作する、その2

前回の記事の対象のプロセスにアクセスする方法ですがいくつかあります。

1:操作するプログラムが対象プロセス自身の場合
2:ローカルで動作しているプロセスでプロセスIDをつかってJMX接続する。
3:リモートで動作しているプロセスでJMXアドレスをつかって接続する。(IDやパスワードがあってもOK)
の3つでしょうか。

今回は1の場合について書いてみます。
1の場合はManagementFactoryからデータを引き出すことができます。

// MBeanServerConnectionを取得
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("java.lang:type=Memory");
MemoryMXBean proxy =
       JMX.newMXBeanProxy(mBeanServer, objectName, MemoryMXBean.class);
System.out.println(proxy.getHeapMemoryUsage());

結果は
init = 0(0K) used = 21876328(21363K) committed = 85000192(83008K) max = 129957888(126912K)

前回の記事で書いた
・java.langに設定されているManagementFactoryから取得できるメモリー状態、CPU状態等のシステムBean
このデータに関してはよけいなことしなくても簡単に引き出せます。
MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
System.out.println(mbean.getHeapMemoryUsage());

結果は同じく
init = 0(0K) used = 21876328(21363K) committed = 85000192(83008K) max = 129957888(126912K)

とこんな感じです。

JMX接続を自作する、その1

さて、3つほどシステムにアクセスして状態を取得してどうのこうのというツールやコマンドの情報をかきましたが、これを自作できないか?と思う人がいると思います。

そして、自作できちゃいます。(さすがにコアな部分はNativeのプログラムにお任せすることになりますけど。)

では、やり方の概要
1:対象のプロセスにアクセスする。(いろんなやり方がある。)
2:MBeanServerConnectionを取得する。(MBeanServerでも良い。)
3:対象のBeanにアクセスして関数を実行したりいろいろする。

3で対象になるになるBeanとしては、次のものがあります。
・JMXにあらかじめ登録しているBean
・HotSpotDiagnostic(heapDumpを実行したりVMのオプションを書き換える)
・java.langに設定されているManagementFactoryから取得できるメモリー状態、CPU状態等のシステムBean
これらです。

例としてはこんな感じ
// JMXにつなぐ。
JMXServiceURL url =
                 new JMXServiceURL("service:jmx:rmi:///jnki/rmi://:9999/test");
JMXConnector connector = JMXConnectorFactory.connect(url);

// MBeanServerConnection取得
MBeanServerConnection mbeanserver = connector.getMBeanServerConnection();

// 一覧取得そして表示
Set<ObjectInstance> mbeans = mbeanserver.queryMBean(null, null);
for(ObjectInstance objInst : mbeans) {
    System.out.println(objInst);
}

CPU状態のダンプ

メモリーに関する記事を書いたので、次はCPUかな。
メモリー状態監視
メモリー状態ダンプ

CPUの状態となると、Thread状態のDumpでしょうか。

環境により、いろいろやり方がありますが今回はLinux用ということにしておきます。

1:SIGQUITをおくって標準出力に出す方法
$ kill -3 [プロセスID]
このコマンドを実行すると、ThreadDumpが取得できますが標準出力を殺すような動作させている場合にはつかえません。

2:jstackを利用する方法
$ jstack [プロセスID]なら関係なく取得することができます。コンソール上に出力する命令ですので、適当なファイルに出力するようにかえてやればOK
$ jstack [プロセスID] > /home/xxxxx/thread.dump
みたいな感じでどうぞ。

3:Java VisualVMを利用する方法
おなじみJavaVisualVM・・・
Threadの項目にThread Dumpというのがあるのでクリックする。

これを利用すると、デッドロックとかが検出できますね。
状態のDumpだけなので忙しいスレッドの取得等はできません。
そこででてくるのがこのコマンド
$ ps -efL | grep [プロセスID]
linuxのpsコマンドにLをいれるとプロセスごとではなくスレッドごとのデータを取得することができるので、上記のコマンドでどのスレッドのLoadAverageがあがっているのか確認することができます。
Threadの識別IDがわかったら、それをHexに直してJava側のDumpデータについているThreadの識別IDと比較してください。一致するThreadが問題のやつということになります。
これを利用すると、無限ループが発生している箇所の特定とかができますよ。

メモリー状態ダンプ

先の記事のメモリー状態監視でメモリーの使われ方を監視する方法を書きましたが、次にきになるのは実際のメモリーがどうなっているのか?ということです。

SunのJavaVMメモリーをDumpする方法がありますのでそれを利用します。
まず基本から
1:JDKについているjconsoleを起動します。
2:次に対象のプロセスにアクセスします。リモートプロセスの場合はservice:jmx:rmi://[]・・・という長いコマンドを入力する必要があります。
3:一番右のMBeanのタブをクリック
4:左のBeans一覧から[com.sun.management]-[HotSpotDiagnostic]-[操作]-[dumpHeap]を選択
5:右のパネルに関数の情報がでてくるので、
p0にファイルを指定[/tmp/message/memorydump.hprof]
p1にtrue/falseを指定[trueでいいと思います。このフラグはdumpするときにGC改修予定のオブジェクトを無視するかどうかのフラグです。]
6:dumpHeapのボタンをクリック
これで対象プロセスが動作しているサーバーにデータが出力されます。

dumpしたデータは作業用PCにダウンロードして、java visualvmを利用して確認します。
1:JDKについているJavaVisualVMを起動します。
2:読み込み→右下の拡張子設定をHeapDumpに設定
3:先ほどのデータを読み込む
これで読み込みが完了します。
(なおVisualVMはjconsoleの動作をかねてる部分もあるので、visualVMからdumpHeapを実行することもできます。(出力先は固定されますけど。))

クラスオブジェクトのもつフィールドデータやクラスオブジェクトを参照しているデータの情報がわかるので、必要のないはずのデータがなぜのこっているのか?どのクラスのデータがたまっているのか?といった情報がするみつけることができます。

ちなみに僕はこれを利用して、SpringのBeanの階層のrootを取得することができました。VisualVMめっちゃ便利です。

メモリー状態監視

Javaでサーバーを組み立てて、長期運用しているとOut of Memoryエラー等がでて、サーバーが飛んだりします。

こんな時はGCの挙動を確認してメモリーの利用量を監視してみましょう。
んで、やり方。

その1:GCのタイミングのときのみ記録する。
Javaを起動するときに特定のフラグをつけると、GCが発生したときにデータを記録してくれます。
-Xloggc:[ファイル名]
をいれるのが一般的かな。
-verbose:gc
というのもあります。

その2:メモリーの状態を記録する。
動作しているJavaのプロセスIDがわかるなら
jstatのコマンドを利用して記録することもできる。
$ jstat -gc [プロセスID] [記録間隔]
オプションは-gc以外にもいろいろあります。
こちらで調査した場合はgcviewerではグラフ化できないので、エクセルなりつかって、グラフ化するといいと思います。

その3:jconsoleやjava visualvmでメモリー利用量のグラフを眺める。jmx経由でjavaの状態を確認できるこれらのツールでプロセスにつなぐと状態をリアルタイムで確認することができます。
ただしGUIツールでデータの保存ができないのが難点(visualVMにはスナップショットの記録というのがあるけど)

ピークになる時間帯をあらかじめ目星つけておいてjstatで記録をとる。
リアルタイムにいろんな情報がとりたいときにはjconsoleやvisualVMで確認する。
この2つで基本せめることができますね。