JNIメモ1

java
この記事は約7分で読めます。

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の環境がインストールされていないとダイアログが表示されて実行することができない。取り急ぎの実行に際しては、以下のリンクから過去のバージョンをダウンロードすることができる。

ダウンロード - Java for OS X 2017-001 - Apple サポート (日本)
Java for macOS 2017-001 は、macOS 10.13 High Sierra、macOS 10.12 Sierra、macOS 10.11 El Capitan、macOS 10.10 Yosemite、macOS 1...

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

コメント

タイトルとURLをコピーしました