Java Flight RecorderとMission Controlのまとめ
Java Flight Recorder(以下JFR)はいわゆるJavaプロセスのプロファイラ。
Java Mission Control(以下JMC)はJFRで取得したプロファイリングデータをGUIで見る事ができるビューア。
もともとはJRockitで搭載されていたのが, Java 7 Update 40以降で搭載されるようになった。
詳しくは
http://www.infoq.com/jp/news/2013/10/misson-control-flight-recorderHotSpot JVMで動作するMission ControlとFlight Recorder
で。
この機能は商用ライセンスのみになっているので、プロダクト用に利用する場合は商用ライセンス契約が必要。
ただし、JVMのオプションを指定することで利用できるようになっているのでお試しで利用することはできるみたい。
使い方は基本的には
http://docs.oracle.com/javase/8/docs/technotes/guides/jfr/toc.htmlTable of Contents
に全てが書いてあります。
プロファイリングの開始
$ java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=duration=60s,filename=myrecording.jfr MyApp
のようにプロセス起動時に開始するか
$ java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder MyApp
で起動してから
jcmd(後述)でJFR.startコマンドを投げることによって開始される。
プロファイリング開始後の操作
jcmdコマンドを使ってjavaプロセスにコマンドを投げることによって、プロファイラに命令を送ることができる
#javaプロセスの確認 $ jcmd 3857 ←プロファイリングしたいプロセス 20762 sun.tools.jcmd.JCmd #このプロセスに投げることができるコマンド一覧の確認 $ jcmd 3857 help 3857: The following commands are available: JFR.stop JFR.start JFR.dump JFR.check VM.native_memory VM.commercial_features ManagementAgent.stop ManagementAgent.start_local ManagementAgent.start Thread.print GC.class_histogram GC.heap_dump GC.run_finalization GC.run VM.uptime VM.flags VM.system_properties VM.command_line VM.version help
プロファイリングスタート
$ jcmd 3857 JFR.start 3857: Started recording 1. No limit (duration/maxsize/maxage) in use. Use JFR.dump recording=1 filename=FILEPATH to copy recording data to file.
recordingという番号がプロファイリング開始番号みたいな扱いらしい。
duration(期間)などの指定もオプションでできる
プロファイリングの状態の確認
$ jcmd 3857 JFR.check Recording: recording=1 name="Recording 1" (running)
recording番号は1でプロファイリング実行中(runnning)というのが確認できる
プロファイリングデータのダンプ取得
$ jcmd 3857 JFR.dump filename="/tmp/myprofile.jfr" compress=true recording=1
filenameでダンプファイルのパスを指定して、zipでの圧縮がしたければcompress=trueを指定する。
recorindg番号を指定は必須
プロファイリングの終了
$ jcmd 3857 JFR.stop recording=1
これもrecorind番号は必須
JMCの起動
$ [JDK_PATH]/bin/jmc
で起動
JMX経由の情報も見ることができるが、今回はプロファイリングデータから読み込むのでメニューのファイルから開くで、ダンプした/tmp/myprofile.jfrを指定する。
時系列ごとによく使われいてるMethodやThreadの一覧、メモリの割り当て状況やIO(ファイルやネットワーク)の状況も全て解析できる。
プロファイリング対象の設定
デフォルトのプロファイリングの設定だとメモリの割り当て状況(Memory -> Allocations)やヒープのオブジェクト解析(Memory -> Object Statistics)がオフになっているので、有効にする。
(JMCでウィンドウ開いてもEvent type 'Allocation in new TRAB' is not enabled in this recordingとかEvent type 'Object Count' is not enabled in this recordingとか出て何もグラフに表示されない)
ちなみにTLABはThread Local Allocation Buffersの略。
$ls -l [JDK_PATH]/jre/lib/jfr -rw-rw-r-- 1 root wheel 18574 Oct 8 2013 default.jfc -rw-rw-r-- 1 root wheel 18531 Oct 8 2013 profile.jfc
これがプロファイリング用の設定ファイルらしい
とりあえずdefault.jrcをmyprofile.jfcとしてコピーして必要そうなところをfalse -> trueに書き換えて有効にする
$ diff default.jfc myprofile.jfc 118c118 < <flag name="class-loading-enabled" label="Class Loading">false</flag> --- > <flag name="class-loading-enabled" label="Class Loading">true</flag> 237c237 < <setting name="enabled" control="heap-statistics-enabled">false</setting> --- > <setting name="enabled" control="heap-statistics-enabled">true</setting> 267c267 < <setting name="enabled">false</setting> --- > <setting name="enabled">true</setting> 446c446 < <setting name="enabled" control="allocation-profiling-enabled">false</setting> --- > <setting name="enabled" control="allocation-profiling-enabled">true</setting> 451c451 < <setting name="enabled" control="allocation-profiling-enabled">false</setting> --- > <setting name="enabled" control="allocation-profiling-enabled">true</setting>
そしてプロファイリング開始時にこの設定を使ってプロファイリング開始するようにする
$ jcmd 3857 JFR.start settings=myprofile
これで先ほどと同じ通りにプロファイリングデータのダンプを取得してJMCに読み込ませれば、該当のプロファイリングが有効になってました。
参考:
Java Mission Control + Flight RecorderをGlassFish4で使ってみた - ブログなんだよもん
Running Java Flight Recorder
[http://hirt.se/blog/?p=370Creating Flight Recordings | Marcus Hirt
[http://hirt.se/blog/?p=381Allocation Profiling in Java Mission Control | Marcus Hirt
IntelliJ Ideaでgradleのサブブロジェクト構成をビルドするとエラーになる問題
Error:scalac: Error: object scala.runtime in compiler mirror not found. scala.reflect.internal.MissingRequirementError: object scala.runtime in compiler mirror not found. at scala.reflect.internal.MissingRequirementError$.signal(MissingRequirementError.scala:16) at scala.reflect.internal.MissingRequirementError$.notFound(MissingRequirementError.scala:17) at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:48) at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:40) at scala.reflect.internal.Mirrors$RootsBase.getModuleOrClass(Mirrors.scala:61) at scala.reflect.internal.Mirrors$RootsBase.getPackage(Mirrors.scala:172) at scala.reflect.internal.Mirrors$RootsBase.getRequiredPackage(Mirrors.scala:175) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackage$lzycompute(Definitions.scala:183) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackage(Definitions.scala:183) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackageClass$lzycompute(Definitions.scala:184) at scala.reflect.internal.Definitions$DefinitionsClass.RuntimePackageClass(Definitions.scala:184) at scala.reflect.internal.Definitions$DefinitionsClass.AnnotationDefaultAttr$lzycompute(Definitions.scala:1024) at scala.reflect.internal.Definitions$DefinitionsClass.AnnotationDefaultAttr(Definitions.scala:1023) at scala.reflect.internal.Definitions$DefinitionsClass.syntheticCoreClasses$lzycompute(Definitions.scala:1153) at scala.reflect.internal.Definitions$DefinitionsClass.syntheticCoreClasses(Definitions.scala:1152) at scala.reflect.internal.Definitions$DefinitionsClass.symbolsNotPresentInBytecode$lzycompute(Definitions.scala:1196) at scala.reflect.internal.Definitions$DefinitionsClass.symbolsNotPresentInBytecode(Definitions.scala:1196) at scala.reflect.internal.Definitions$DefinitionsClass.init(Definitions.scala:1261) at scala.tools.nsc.Global$Run.<init>(Global.scala:1290) at xsbt.CachedCompiler0$$anon$2.<init>(CompilerInterface.scala:116) at xsbt.CachedCompiler0.run(CompilerInterface.scala:116) at xsbt.CachedCompiler0.run(CompilerInterface.scala:102) at xsbt.CompilerInterface.run(CompilerInterface.scala:27) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:102) at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:48) at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41) at org.jetbrains.jps.incremental.scala.local.IdeaIncrementalCompiler.compile(IdeaIncrementalCompiler.scala:28) at org.jetbrains.jps.incremental.scala.local.LocalServer.compile(LocalServer.scala:25) at org.jetbrains.jps.incremental.scala.remote.Main$.make(Main.scala:58) at org.jetbrains.jps.incremental.scala.remote.Main$.nailMain(Main.scala:21) at org.jetbrains.jps.incremental.scala.remote.Main.nailMain(Main.scala) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.martiansoftware.nailgun.NGSession.run(NGSession.java:319)
対応方法
$ cd {project_root} $ find . -name "*.iml" | xargs rm -rf #idea設定を全削除 $ find . -name ".idea" | xargs rm -rf # ideaプロジェクト設定を削除 $ gradle clean # gradleきれいにしてから $ gradle build -x test # 一度、全ビルドしてからの $ gradle idea # idea設定を作成
IntelliJ IDEA13 CEのコード実行(Run)の種類
ささっと書いて実行するときにどのショートカットキー(keymap)を使っていいのかわからなかったので。
※MacOSです
コンフィグレーションを選択して実行
Option + Shift + F10
(前回と同じものを)実行
Shift + F10
現在の状況を実行
※現在の状況 = アクティブなエディタや選択中のファイルで実行可能な形式(extends AppなのかUnitTestなのかとかをよしなに判断してくれるっぽい)
Ctrl + Shift + F10
なので、基本はCtrl + Shift + F10を連打しときゃいいのかな
IntelliJ 13 CE + Scala + Gradleでのプロジェクト作成
こんなクールな操作で開発ができるようになりたい
intellij CEをダウンロード
コミュニティエディションをダウンロードしてきます
http://www.jetbrains.com/idea/download/index.html
Gradleプロジェクトを作成
- Use Auto-import(build.gradleを修正すると各種インポートが自動的に行われるっぽい)
- Create directories for empty content roots automatically(今回で言うとScalaプラグインを作成した時のディレクトリ構造が自動的に追加されるっぽい)
- User default gradle warpper(Mac上のgraldeではなくgradle-wrapperを使う)
build.graldeにscala Pluginを追加
build.graldeをエディタで開いて
apply plugin: 'scala'
を追記する
「Create directories for empty content roots automatically」が有効になっていると自動的にsrc/main/scalaディレクトリが自動で作成される
build.gradleにscala-compilerまわりのdependencyを追加
apply plugin: 'java' apply plugin: 'scala' sourceCompatibility = 1.5 version = '1.0' repositories { mavenCentral() } ext { scalaVersion = '2.10' scalaVersionRevision = '3' scalazVersion = '7.0.5' specs2Version = '2.3.4' } dependencies { compile "org.scala-lang:scala-compiler:$scalaVersion.$scalaVersionRevision" compile "org.scala-lang:scala-library:$scalaVersion.$scalaVersionRevision" compile "org.scala-lang:scala-reflect:$scalaVersion.$scalaVersionRevision" }
eclipseを使ってvert.xを起動する
Eclipseでvert.xをローカルで起動するのに、 Gradle テンプレートを使うってのは見かけたけどmavenプロジェクトの記事があまり見当たらなかったので覚え書き。
Eclipse Java EE IDE for Web Developers.Version: Juno Service Release 2
mavenはm2eプラグインを使用。
eclipseでmavenプロジェクトを作成して、Archetypeをvertxを選択する。
maven-dependency-plugin (goals “copy-dependencies”, “unpack”) is not supported by m2e
というpom.xmlのエラーが出るので
こちらの書き込みに従い
これでプロジェクトのエラーが消えるので、試しにfooパッケージに
package foo; import org.vertx.java.core.Handler; import org.vertx.java.core.http.HttpServerRequest; import org.vertx.java.platform.Verticle; public class HelloWorldVerticle extends Verticle { public void start() { container.logger().info("Verticle start."); vertx.createHttpServer().requestHandler(new Handler<HttpServerRequest>() { public void handle(HttpServerRequest req) { req.response().headers().set("Content-Type", "text/plain"); req.response().end("Hello World!"); } }).listen(8080); } }
というテストコードをServer.javaという名前で置いてみる。
さらに
src/main/resources/mod.jsonの
"main":"foo.PingVerticle",
を
"main":"foo.Server",
に変更する
eclipseのプロジェクトを右クリックRunからmaven buildで起動する。
※CLIの場合はmvn vertx:runModEclipseを叩く。
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Project - testio 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- vertx-maven-plugin:2.0.0-final:runModEclipse (default-cli) @ testio --- [INFO] CTRL-C to stop server
とかログが出るので,ブラウザからlocalhost:8080でhello worldが出ればおk
pythonでJpeg画像のexifを編集する
ある時子供を撮影するのによく使っているデジカメNEX-5の初期設定を誤ったらしく、全部の写真の日付が一年前になっていることに気づいた。
印刷する時に日付を入れる場合は全部一年ずれて日付がついてしまうので、今までNEX-5で撮った写真のexifを書き換えるためのスクリプトを書いた。
とりあえずpythonでexifをいじるために調べて最初に出てきたのはPython Imaging Libraryだったが、どうやらexifを読み込む機能しかないらしい。
http://www.pythonware.com/products/pil/
pyexiv2というのがイケてるらしいのでこれでスクリプトを書くことにする。
Python で JPEG の Exif などを扱う
チュートリアル
Mac上にvagrantでubuntu12環境を用意した上でpyexiv2をインストールする
apt-get install python-pyexiv2
これで終わり。
あとはpyexiv2を使った普通にexifの日付情報を一年進めて書き換えた上で保存すればOK
import sys import os import stat import datetime import pyexiv2 targetDir = "/home/vagrant/100MSDCF_2/" count = 0 for fileName in os.listdir(targetDir): f = targetDir + fileName s = os.stat(f) times = (s[stat.ST_MTIME], s[stat.ST_MTIME]) mtime = datetime.datetime.fromtimestamp(s[stat.ST_MTIME]) metadata = pyexiv2.ImageMetadata(f) metadata.read() dateTimeTag = metadata['Exif.Image.DateTime'] dateTimeOriginalTag = metadata['Exif.Photo.DateTimeOriginal'] dateTimeDigitizedTag = metadata['Exif.Photo.DateTimeDigitized'] thumbnailDateTimeTag = metadata['Exif.Thumbnail.DateTime'] dateTimeTag.value = dateTimeTag.value + datetime.timedelta(days = 366) dateTimeOriginalTag.value = dateTimeOriginalTag.value + datetime.timedelta(days = 366) dateTimeDigitizedTag.value = dateTimeDigitizedTag.value + datetime.timedelta(days = 366) thumbnailDateTimeTag.value = thumbnailDateTimeTag.value + datetime.timedelta(days = 366) print "convert done " + f + " ->" + dateTimeTag.raw_value metadata.write() count += 1 print str(count) + " files done"
それにしてもぱぱっとubuntuとか必要な環境を作れちゃうvagrantはほんと便利ですなー。
subversionを使ったA successful Git branching modelの運用
現在のプロジェクトではsubversionを使っているのですが、10人ほどの開発メンバーで並行開発をしているためA successful Git branching modelを参考にFeature branchesを活用した開発スタイルにしています。
A successful Git branching model を翻訳しました
しかしどうしてもブランチの統合時に意味不明なコンフリクト(競合)が発生してしまい、その度に手動(というか目視やメンバー同士の確認)でのマージを行なっておりリリースや統合が高コストになっていました。
例えば以下のような場合
2.のマージの時にfeature1ブランチにhello.jpgがコミットされたという変更履歴が残るため、3.のdevelopへのマージの際にはfeature1ブランチの変更点(コミット)を全てマージしようとするため、再度developにhello.jpgをコミットしてしまうようです。
調べた所reintegrateというオプションを見つけました。
ブランチをトランクにマージするときは、--reintegrate オプションを付ける。
- reintegrate オプションを使うこの形式のマージは一つのブランチにつき一回だけ行える (その後ブランチを更新してもそれをトランクにマージできないので、ブランチはただちに削除するとよい)。ブランチでの変更のうち、トランクからブランチへのマージによるもの以外の変更がトランクに適用され、自動的に svn:mergeinfo 属性が更新される。
Eclipseのsubversiveを使っている場合はマージウィンドウのタブに該当の機能がありました。
ここからマージ作業を行えば、晴れてブランチでの修正分のみがdevelopに取り込めましたとさ!
追記:
mergeの方法についてなのですがdevelop -> featureブランチへの取り込み時に手動でマージ(ファイルを上書きするとか差分エディタで書き込むとか)すると、svn管理上のmergeinfoというプロパティがセットされません。
そのため、reintegrateマージの際に上記コミットがマージコミットとして認識されずに、developへのマージ時にコンフリクトしてしまいます。
developからfeatureブランチへの取り込みの際は必ずmergeコマンド(or eclipseのマージ機能)を使うようにしましょう。
http://www.asahi-net.or.jp/~iu9m-tcym/svndoc/svn_mergeinfo.html