java」カテゴリーアーカイブ

Javaで作成した一時ファイルを終了時に削除する

この記事は【2016年10月6日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

Javaのjava.nio.file.Filesで作った一時ファイルをプログラム終了時に削除するのにFilesクラスにそれ用のAPIがあるかと思っていたけどなかったので以下のようにして対応した。

File#deleteOnExit

FileのAPIを使う方法。

shutdown-hook

シャットダウンフックを使う方法。

StandardOption.DELETE_ON_CLOSE

こちらはcloseメソッドが呼ばれた時に削除されるらしい。以下ドキュメントの引用。

閉じるときに削除します。このオプションがある場合、実装はファイルが適切な close メソッドによって閉じられるときに、ベストエフォートでファイルを削除しようとします。close メソッドが呼び出された場合、Java 仮想マシンの終了 (『Java 言語仕様』の定義どおりに正常終了か、可能な場合は異常終了) 時にファイルを削除するためのベストエフォートの試みが行われます。

参考リンク

改訂2版 パーフェクトJava

改訂2版 パーフェクトJava

posted with amazlet at 16.11.26
井上 誠一郎 永井 雅人
技術評論社
売り上げランキング: 46,819

JNIメモ1

この記事は【2016年6月16日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

Impalaという分散クエリエンジンを使っていてImpaladが突然JVMのSIGSEGVで異常終了してしまうことがありソースを追いかけている。ソースを読んでいるとJNI経由でC++の世界とJavaの世界との連携を行なっている部分があり、JNIについて知識が乏しかったので今回JNI(Java Native Interface)に関して自分なりに調べ以下備忘録としてまとめてみる。

JNI(Java Native Interface)

Javaの世界とC/C++の世界との間をつないでくれる。JavaとC/C++のようなネイティブで実行されるプログラム間で連携するためのインターフェース仕様。JavaからC/C++で書かれたプログラムを呼び出したり、逆にC/C++からJavaのオブジェクトを呼び出したりすることが可能。

Quick Start

まずは、簡単なサンプルを動かしてみて実行イメージをつかむ。

JavaからC/C++のプログラムを呼ぶ

以下のステップで実行する。

  1. nativeメソッドを定義し、javacでJavaプログラムをコンパイル
  2. javahコマンドでヘッダーファイル生成
  3. c/c++の実装部分を書く
  4. ダイナミックライブラリ(Shared library)を生成
  5. JavaからSystem.loadLibraryでダイナミックライブラリをロード
  6. 実行

まずは、nativeメソッド定義する。ここでは、引数に渡した文字列を出力する単純なサンプルを想定している。

続いてコンパイルする。

クラスファイルからjavahでヘッダファイルを生成する。

これにより生成されたファイルは以下のようになる。

メソッド名は、以下のような規則で構成されるらしい。

  • 接頭辞Java_
  • 分解された完全修飾クラス名
  • 下線 (「_」) 区切り文字
  • 分解されたメソッド名
  • オーバーロードされたネイティブメソッドでは、2 個の下線 (「__」) に続いて分解された引数のシグニチャー

続いて実装。C++で書くと多少ポインタ周りがスッキリする。各種関数がインラインメンバ関数として定義されているため(jni.h参照のstruct JNIEnv_を参照)

ダイナミックライブラリを作成する。

最後にJavaを実行して正常にロードされているか確認。

C/C++からJavaのプログラムを呼ぶ

続いて今度はC/C++からJavaのプログラムを呼び出す例を見てみる。基本的な流れは以下のようになるだろうか。

  1. VM作成
  2. クラス探索
  3. メソッドの呼び出し
  4. VM破棄

以下実際のソースで見てみる。ここでは、java.lang.Mathクラスのrandomメソッドを実行してみる。

続いてコンパイル。

なお、Macで実行した場合Java SE 6の環境がインストールされていないとダイアログが表示されて実行することができない。取り急ぎの実行に際しては、以下のリンクから過去のバージョンをダウンロードすることができる。

https://support.apple.com/kb/DL1572?viewlocale=en_US&locale=ja_JP

Type Mapping

JavaとC/C++の世界ではルールも異なればデータの型も異なる。したがって両者の間で型をどのように扱うのか、あらかじめ取り決めをしてやる必要がある。jni.hを参考にすると以下のようにマッピングされていることがわかる。

プリミティブ型

Javaの型 ネイティブの型 説明 Ex) jni.h / jni_x86.h
(OpenJDK9)
boolean jboolean unsigned 8bit unsigned char
byte jbyte signed 8bit signed char
char jchar unsigned 16bit unsigned short
short jshort signed 16bit short
int jint signed 32bit int
long jlong signed 64bit #if defined(_LP64)
long
#else
long long
#endif
float jfloat 32bit float
double jdouble 64bit double
void void

その他以下のようなタイプエイリアスやマクロが定義されている。

参照型

以下のような構造になっている。

Signature

JVMの型のシグネチャー表現は以下のようになっている。.classファイルを開くと見かける英数字や記号などのアレ。

以下jvm.hからの抜粋。

上の表現でなんとなくわかるが、まとめると以下のようになる。

Type Signature Javaのデータ型
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L fully-qualified-class; 完全修飾指定
Ex) java.lang.String;
[type 配列
Ex) int[]の場合
[I
(arg-types)ret-type メソッドの型
Ex) void main(int argc, String[] args);
(I[Ljava.lang.String;)V
V void

これにより、あらためてC/C++からのJavaプログラムを実行するのには以下のようになる。

参考リンク

  • https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/functions.html
  • http://www.ne.jp/asahi/hishidama/home/tech/java/jni.html
  • https://ja.wikipedia.org/wiki/Java_Native_Interface
  • https://newcircle.com/bookshelf/java_fundamentals_tutorial/_java_native_interface_jni
  • https://support.apple.com/kb/DL1572?viewlocale=en_US&locale=ja_JP
  • https://github.com/cloudera/Impala
JNI:Java Native Interfaceプログラミング―C/C++コードを用いたJavaアプリケーション開発 (Java books)
ロブ ゴードン
ピアソン・エデュケーション
売り上げランキング: 547,674
Java仮想マシン仕様 (The Java series)
ティム リンドホルム フランク イェリン
ピアソンエデュケーション
売り上げランキング: 462,364

Building OpenJDK9 on Mac

この記事は【2016年6月6日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

Javaをデバッグ実行したくOpenJDKをmac上でビルドした時のメモ。僕の環境は以下の通り。

ビルド環境

  • OS X EL Captain 10.11.5
  • XCode7.3.1
  • JDK1.8.0_77

  • OpenJDK9

ビルド

OpenJDKのリポジトリはmercurialでホスティングされているのでインストールする。

続いてOpenJDK9のビルド。OpenJDK9からは、clangコンパイラに対応しているらしい。README-builds.htmlを読むと、以下のようなビルド環境が前提となっている。

Base OS and Architecture OS C/C++ Compiler Bootstrap JDK
Mac OS X X64 (64-bit) Mac OS X 10.9 “Mavericks” Xcode 6.3 or newer JDK 8

無事にビルドが終わるとbuildディレクトリ下に各種バイナリファイルが生成される。

デバッグ実行

今回は、lldbを使って行なう。ちなみに、gdbの場合はコード署名が必要となる(参考リンク参照)。

Javaのサンプルプログラムは以下の通り。

続いてサンプルプログラムをコンパイルしjavaを実行してみる。

ステップ実行していくと、get_cpu_info_stub(&_cpuid_info);の所でSIGSEGVで落ちる。。この先は深くてまだ追えていない。

debug-xcode

参考リンク

  • http://openjdk.java.net
  • http://jcdav.is/2015/08/26/building-a-debug-jvm/
  • https://bugs.openjdk.java.net/browse/JDK-8152856
  • http://d.hatena.ne.jp/torazuka/20150102/buildjdk9
  • http://qiita.com/takahashim/items/204ffa698afe09bd4e28
  • http://lldb.llvm.org/lldb-gdb.html

How lombok works

この記事は【2014年12月16日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

アノテーションさえ十分に使いこなせていないが、、javaのlombokライブラリについて調べる機会があったので自分なりにまとめてみる。

lombokは、AST変換というマジックを使ってコンパイラの変換プロセスの中で生成されたAST(抽象構文木)を操作し、アノテーションに対応するコードを差し込んでいるようだった。ソースからざっくりとした流れを追ってみる。

動作の流れ

javacの変換の流れを見てみる。

  • META-INF/servicesjavax.annotation.processing.Processorlombok.launch.AnnotationProcessorHider$AnnotationProcessorprocessが呼ばれる。processメソッドを見ると、実際の処理はcreateWrappedInstanceメソッドで生成されるオブジェクト(lombok.core.AnnotationProcessorクラス)に転送される。
  • lombok.core.AnnotationProcessorprocessメソッドからlombok.javac.apt.Processorprocessメソッドが呼ばれる。
  • その中から、lombok.javac.JavacTransformertransformが呼ばれるが、その前にRoundEnvironmentgetRootElementsメソッドで得られるjavax.lang.model.element.Elementのオブジェクトは、com.sun.tools.javac.tree.JCCompilationUnitに変換されている。
  • transformメソッドで、JCCompilationUnitのオブジェクトは.lombok.javac.JavacASTクラスでラップされ、lombok.javac.JavacTransformer$AnnotationVisitorでASTをトラバースし、lombok.javac.JavacAnnotationHandlerクラスのハンドラーを呼んで変換される。

Javac AST

非公開API?が使われている。mainメソッドを書き換えてみる。

com.sun.tools.javac.util.Listでappendしてもサイズが増えないのは何故?よくわからない。。上では、最後のBlockの初期化でJCExpressionStatementを渡している。

Eclipse AST

EclipseのJDTは、XML DOMモデルと同じ考え方の独自のDOMとASTを持っているらしい。DOMと同様な考え方で、コードブロックに任意のStatementを挿入したりすることができる。以下は、mainメソッドにHello, Worldを出力するStatementを挿入し、コンパイルして実行する例。

Manipulates AST with JDT

CompilationUnitは、コンパイル単位でソースファイルに該当する。createASTメソッドでは、ASTNodeクラスのオブジェクトが返ってくるが、CompilationUnitASTNodeクラスを継承しているためKindをK_COMPILATION_UNITに指定すればキャスト可能である。その後、ASTVisitorでASTをトラバースし、メソッド定義のブロックにSystem.out.printlnStatementを差し込んでいる。

jarファイルたち

どのクラスがどのjarに含まれているのかわかりづらかった。簡単に調べられる方法はあるのだろうか。。力技で探していった。

必要だったのは以下のjarたち。

参考リンク