2015年11月15日日曜日

java.lang.instrument

JVMにはインストゥルメンテーションという機能があって、実行中にバイトコードを書き換えることができる(元々は実行中のプログラムの計測のためのものらしい)。
https://docs.oracle.com/javase/jp/8/api/java/lang/instrument/package-summary.html

 プログラマーがバイトコードを書き換えるためには、エージェントというものを作る。エージェントを書き換え対象のJVMに適用するには2通りの方法がある。
  1. JVMの実行時に-javaagentオプションによってエージェントJARを指定する。-javaagentは複数指定できる。
  2. すでに起動済みのJVMに対してエージェントを適用する場合は、別JVMとしてエージェントを起動して起動済みのJVMにアタッチする。jconsoleがこちらのパターン。Attach APIというものを使うようだが、これは開発ベンダー依存APIなので注意。
バイトコードを書き換えることができるので、普通のプロダクトではできないようなある意味、黒魔術っぽいことができるようになる。このインストゥルメンテーションを使っている有名なプロダクトを調べてみた(ドキュメントで調べただけで実際には1つも試してはいない)。
  • JRebel・・・サーバー上にデプロイするアプリケーションのように長時間起動するアプリケーションを開発中にソースコードを書き換えた時にIDEがコンパイルしたバイトコードを使ってサーバーが使っているバイトコードを置き換えることで、リアルタイムに変更を反映できる。いわゆるホットスワッピングとかホットコード置換とかホットデプロイと呼ばれるもの。デバッガーがコードを変更するのと似ているが、適用できる範囲が違うらしいSpring-Loadedも似ている。販売元による比較がこちらにある。Seaser(S2Container)はDIコンテナだからエージェントを使ったアプローチではないけど実現しようとしていることは似ている。
  • NewRelic・・・JVMに限らずソフトウェアの監視を行うサービスだが、JVMアプリケーションを監視する場合は、-javaagentで指定する。ログを監視サーバーに送るのかな。
  • Lombok・・・ お決まりの「ボイラープレート」コード削減することができる。EclipseなどのIDEに導入するときに-javaagentで指定する。エージェントだけでなくjavax.annotation.processingも使用して実現している。
  • JMockit・・・テスト時のモックを作るためのツール。
  • JaCoCo・・・テスト時のカバレッジ測定ツール。
  • AspectJ・・・アスペクト指向言語、ツール。
インストゥルメンテーションを使うと色々面白いことができるけど、リスクも多い。考慮点は以下の通り。
  1. 変更されたバイトコードそのものを見ることはできないから、ソースコードに書いた通りに動くことを求めているシチュエーションでは使うべきではない。つまり挙動そのものを書き換えないユースケースのみで使う方が安全。Lombokにはdelombokという機能で書き換えたソースコードを生成できる。NewRelicは性能監視しかしないので、副作用は限定的なはず。他のプロダクトは開発時やテスト時に使うのでほとんどの場合、実運用では使わない。AspectJは実運用でも使えてしまうので注意が必要。
  2. 複数のインストゥルメンテーション製品を使うとちゃんと動かないことがある。しかもエラーがわかりにくく、はまりやすい。
  3. リフレクションを使うよりは速い
  4. 学習コストが高い。バイトコードを操作する機能を作る時は、ASM、BCEL、Javassistなどを使うがどれも簡単ではない。例えばASMを使う場合はバイトコードとJVMの動作の仕組みを理解する必要がある。

0 件のコメント:

コメントを投稿

DockerでMQ環境を作る

DockerHubの ibmcom/mq を使う。ファイルシステムとしてtmpfsは使えないので これ を参考に/mnt/sda1に置き換える。 brew install docker docker-machine docker-machine create --drive...