Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

Javaにおけるネイティブコード連携の各種手法の紹介

10.731 visualizaciones

Publicado el

JJUG CCC 2015 Fallの「Javaにおけるネイティブコード連携の各種手法の紹介」での発表資料です。
#jjug_ccc #ccc_cd7

Publicado en: Tecnología
  • Sé el primero en comentar

Javaにおけるネイティブコード連携の各種手法の紹介

  1. 1. Javaにおける ネイティブコード連携の 各種手法の紹介 NTTレゾナント・テクノロジー(株) 久納 孝治(ひさのう こうじ) hisano
  2. 2. 自己紹介  久納 孝治 (ひさのう こうじ)  NTTレゾナント・テクノロジーでRemote TestKitを作っています。 「今まで作ったもの」  世界初のケータイJava向けGameBoyエミュレータ (2002年のJavaOneで発表) http://www.itmedia.co.jp/mobile/0209/26/n_game.html  Skype社の公認のオフィシャルAPIに選定されたSkype4Java http://www.itmedia.co.jp/enterprise/articles/0710/05/news132.html  Pure JavaによるDalvik VMインタープリタ http://code.google.com/p/android-dalvik-vm-on-java/  MIDP→iアプリ変換ツール (Opera Miniのiアプリ版で採用) http://www.itmedia.co.jp/mobile/articles/1106/15/news106.html など
  3. 3. Pure JavaによるDalvik VMインタープリタ  AndroidのDalvik VMの仕様書から実装  Java ME上で動作するインタープリタ (知る限りではクリーンルーム実装では世界初)  サポートしている機能 Dalvik VMの全命令 Java ME CLDCのAPI群 wait・notify等を含むマルチスレッド関連(グリーンスレッドで実現)
  4. 4. MIDPアプリ→iアプリ変換ツール  Java MEのMIDPアプリのjarをiアプリのjarに静的変換するツール + MIDP APIのランタイム から構成  静的変換ツールはjavassistライブラリを用いて実装 ・javax.microeditionのクラス参照をtv.hisano.doja.runtime.midpパッケージに変換 ・System#getProperty呼び出しを独自のクラスメソッド呼び出しに変換 ・Class#getResourceAsStream呼び出しを独自のクラスメソッド呼び出しに変換 ・などなど  MIDP APIのランタイムをクリーンルーム実装 ・ピュアJavaのPNGデコーダを組み込み ・OpenGLを用いた高速な画面描画コンポーネントを組み込み  Opera社とOpera miniのiアプリ版としてリリース(現在は公開終了)
  5. 5. 本編
  6. 6. Javaでネイティブコードを使う意義  Javaで書かれていない豊富な各種ライブラリの利用 例: TensorFlow、Bonanza 弊社での利用例: コンピュータビジョンライブラリ(OpenCV)、iOS端末管理 (libimobiledevice)、ChromeのPDFエンジン(Pdfium)  OS特有の機能の利用 例: Windowsのジャンプ・リスト、OS Xの通知センター 弊社での利用例: プロセス情報取得(winp)、UNIXドメインソケット(junixsocket)  GPU・CPUをフルに使った高速化 例: DirectX、WebRTC 弊社での利用例: 音声・ビデオコーデック(OpenH264)  既存資産のポーティングの手間の軽減
  7. 7. ネイティブコードを組み込む方法 開発工数 品質 できること ① ラップした既存ライブラリを利用 ◎ ? △ ② Javaのみでネイティブ連携を行えるライブラリ を利用 ○ ○ ○ ③ JNIを利用 △ △ ◎
  8. 8. ラップした既存ライブラリを利用
  9. 9. GitHubで既存ライブラリを検索  アドバンスド検索 https://github.com/search/advanced  プレフィックスを用いた絞り込み https://help.github.com/categories/search/ 他の応用例: jnrとjnaどちらが使われているか? jna filename:pom.xml fork:false→3263件 jnr-ffi filename:pom.xml fork:false→47件
  10. 10. 利用例: JavaCV  JavaからOpenCVを使えるライブラリ  OpenCVにも本家Javaライブラリがあるものの、セットアップの容易さからJavaCV がオススメ  Apacheライセンス or クラスパス例外ありのGPLのデュアルライセンス 類似領域検索での利用例
  11. 11. 利用例: winp  JavaからWindowsのプロセス管理を行うためのライブラリ  Jenkinsを作られている川口さん作でMITライセンス  サポートしている機能  プロセス情報の取得  プロセスの強制終了  プロセスの優先順位変更  OSからのログオフ・リブート など プロセス一覧を表示するコード
  12. 12. Javaのみでネイティブ連携コードを 書けるライブラリを利用
  13. 13. この手法のメリット  JNIで連携コードを別途書く必要なし  OSごとのネイティブライブラリの準備が不要  Javaのデバッガを用いての開発が可能
  14. 14. com4j  COMコンポーネントをJavaから使えるようにするライブラリ  Jenkinsを作られている川口さん作  BSDライセンス ライブラリを生成 ライブラリを利用
  15. 15. SWTのinternal API  EclipseのGUIライブラリのSWTの下回り(org.eclipse.swt.internal)には大量のOS特 有コード  Windows: Win32/Win64  OS X: Cocoa Skype4Javaでの利用例
  16. 16. JNA  JavaインターフェイスでC関数を定義するとバインドしてくれるライブラリ  Apacheライセンス or LGPLのデュアルライセンス  似たライブラリとしてJNRあり ① JavaインターフェイスでC関数を定義 ② インターフェイス定義からプロキシを生成 ③ 実際の処理で利用
  17. 17. JNAのnativeメソッド定義による利用  nativeメソッドを定義したクラスをNative#registerメソッドに渡すだけで、JNIラ イブラリを用意したように呼び出すことが可能に pdfium4jでのnativeメソッド定義による利用例
  18. 18. JNAでのJavaリスナーの登録  Javaのリスナーを、Cの関数ポインタのようにコールバックとして登録可能
  19. 19. JNAでのAPI差分の吸収  JNAの各種処理をフックしてAPI差分を吸収することが可能 ・Javaのインスタンス→ネイティブオブジェクトの対応関係変更(TypeMapper) ・Javaのメソッド→C関数名の対応関係変更(FunctionMapper) ・C関数の呼び出し処理の前後に処理を追加(InvocationMapper) C関数名の変更を吸収する例
  20. 20. JNAerator  ヘッダーファイルからJNAを用いたコードを生成してくれるツール  JNAだけでなく、BridJ・Rococoa・node.js向けのコード出力も可能
  21. 21. JavaCPP  Javaクラス + アノテーションでC++クラスを定義すると、クラス定義を元にJNI コードを生成して使えるようにしてくれるライブラリ  JavaCVの下支えのライブラリ  Apacheライセンス or GPLのデュアルライセンス  javacpp-presetsリポジトリに豊富な定義が存在(CUDA、FFmpeg等)  TensorFlowの定義も追加! おそらく次のバージョンで楽に使えるように。 https://github.com/bytedeco/javacpp- presets/blob/master/tensorflow/src/main/java/org/bytedeco/javacpp/tensorflo w.java
  22. 22. 今まで紹介した手法の問題点  不正なポインタを利用するとプロセスごと落ちてしまう問題あり  各OS・CPUアーキテクチャごとにネイティブライブラリを用意する必要あり LLVMを調査している時にいい手法を思いついたので、 j2js2nc(Java to JavaScript to Native Code)ライブラリ を作ってみました!
  23. 23. LLVMの紹介  コンパイラ開発に必要な各種コンポーネントを提供するコンパイラ基盤 Clang: C/C++/Objective-Cから中間表現のIRを生成するコンポーネント LLVM Core: IRから各種CPU向けバイナリを生成するための各種コンポーネント LLDB: ClangやLLVM Coreを使って実装されたデバッガ  BSDライセンスに似た使いやすいライセンス  (ユーザ目線では新しいコンパイラと捉えるのがいいかも) Clang C/C++フロントエンド LLVM Optimizer x86バックエンド ARMバックエンド PowerPCバックエンド
  24. 24. Emscriptenの紹介  C/C++コードをJavaScriptにコンパイルするツール Unity等での応用例: https://github.com/kripken/emscripten/wiki/Porting- Examples-and-Demos Win 32 APIでの応用例: https://github.com/klutzy/win32.js MS-DOSゲーム: https://archive.org/details/softwarelibrary_msdos_games  Windows・OS X・Linux向けに提供  LLVMのバックエンドでCPU特有のバイナリではなく、JavaScriptコードを生成  JavaScriptサブセット仕様のasm.jsを用いて高速化 Clang C/C++フロントエンド LLVM Optimizer Emscriptenバックエンド(Fastcomp)
  25. 25. Emscriptenによるwin32.jsの実現方法 JavaScriptコード Emscriptenで変換 fake-mswin - JavaScriptライブラリ Win32エミュレーションライブラリ
  26. 26. j2js2ncライブラリの紹介  j2js2ncはEmscripten版JNA  Emscriptenを利用するメリット  ネイティブコードが、マシン語ではなくJavaScriptとなるため、OSやCPUに非依存  Nashorn上でマシン語相当が動作するため、不正なポインタによってプロセスが終了する ことがない  JNA 4.1の下回りを実装し直して実現しているため、JNAの豊富な高度な機能はそ のまま利用可能 Javaインスタンスの自動マッピング メソッド→関数のマッピングルール変更 など C/C++コード JavaScript Java Emscriptenで変換 生成されたJavaScriptコードはJava 8から搭載されたNashorn上で実行 Javaインターフェイス + j2js2ncでC関数を自動バインド
  27. 27. j2js2ncの具体的な利用の流れ #include <stdio.h> #include <stdlib.h> void hello(char *name) { printf("Hello, %s!n", name); } $ emcc hello.c -s EXPORTED_FUNCTIONS="['_hello']" -o js/hello.js package jp.hisano.sample; import jp.hisano.j2js2nc.Library; import jp.hisano.j2js2nc.Native; public class Main { public interface Hello extends Library { void hello(String name); } public static void main(String[] args) throws Exception { Hello library = (Hello) Native.loadLibrary("hello", Hello.class); library.hello("j2js2nc"); } } hello.dll等に相当するhello.jsを生成 Libraryインターフェイスを継承してメソッドを定義するだけ でC関数が利用可能
  28. 28. JNAとj2js2ncの内部処理の違い
  29. 29. JNAでの内部処理 #include <stdio.h> #include <stdlib.h> void hello(char *name) { printf("Hello, %s!n", name); } Hello library = (Hello) Native.loadLibrary("hello", Hello.class); library.hello("j2js2nc"); JNA処理 1. StringインスタンスをJNAオブジェクトに変換(NativeString インスタンスの生成) 2. JNAオブジェクトからネイティブオブジェクトに変換 (malloc関数でバイト配列を確保) 3. libffi経由でネイティブライブラリの関数を呼び出し GC実行時 1. finalizeメソッド実行時にfree関数が呼び出されて解放
  30. 30. j2js2ncでの内部処理 #include <stdio.h> #include <stdlib.h> void hello(char *name) { printf("Hello, %s!n", name); } Hello library = (Hello) Native.loadLibrary("hello", Hello.class); library.hello("j2js2nc"); J2js2ncの処理 1. Stringインスタンスをj2js2ncオブジェクトに変換 (NativeStringインスタンスの生成) 2. j2js2ncオブジェクトからネイティブオブジェクトに変換 (_malloc関数でバイト配列を確保) 3. javax.script経由でJavaScript関数を呼び出し GC実行時 1. finalizeメソッド実行時に_free関数が呼び出されて解放 JNA処理 1. StringインスタンスをJNAオブジェクトに変換(NativeString インスタンスの生成) 2. JNAオブジェクトからネイティブオブジェクトに変換 (malloc関数でバイト配列を確保) 3. libffi経由でネイティブライブラリの関数を呼び出し
  31. 31. いろいろな問題とその解決方法
  32. 32. ポインタのサポート ポインタは何バイト?  Emscriptenでは32-bitマシンとしてコンパイル  long型・wchar_t型・size_t型も、すべて32-bit 定義の初期化のメソッド
  33. 33. 64-bit整数型のサポート JavaScriptは64-bit浮動小数点型のみ、64-bit整数型の引数や戻り値は?  一つの64-bit整数引数を、二つの32-bitビット整数(実体は64-bit浮動小数点型)に分割  戻り値の64-bit整数の上位32-bitはtempRet0というグローバル変数に入れて返却 戻り値の処理 引数の処理
  34. 34. 処理速度が遅い 1バイトのメモリアクセスごとにJavaScriptのパース・実行処理が行われて低速 Nashornの内部APIを直接使う形にして高速化 (BufferArgumentsMarshalTestテストの実行時間が20秒から2.5秒に短縮!) 変更後変更前
  35. 35. 様々なネイティブ連携手法があるJavaは 最高です! ご清聴ありがとうございました。

×