Learn · concepts
JNIとは?モッダー向けのJavaネイティブインタフェース
JNI(Java Native Interface)は、JVMからC/C++を呼び出し、その逆を行うためのJavaに組み込まれたインターフェースです。MinecraftのModがネイティブライブラリをロードする標準的な方法です.
JNIとは、平易な言葉で言うと何ですか?
JNI(Java Native Interface)は、Javaの中で、JVM上の管理されたバイトコードと、CまたはC++で記述されたコンパイルされた機械語という、2つの世界をつなぐ部分です。メソッドにnativeキーワードが付き、かつ本体がない場合、実行時にJVMがロードされたネイティブライブラリ内の対応する関数にリンクします。そこから両者が引数や戻り値を交換します。
この境界線は一方通行ではありません。ネイティブ関数はJNIEnvポインタを受け取り、そのハンドルはJavaへのゲートウェイとなります。これにより、Javaのフィールドの読み書き、Javaメソッドの呼び出し、オブジェクトの構築、例外の発生といった呼び出しが可能になります。つまり、Javaが外部に呼び出すための橋が、Cが内部に呼び込むための橋でもあるのです。
MinecraftのModやクライアントがネイティブコードに触れるのはなぜですか?
ほとんどのJavaではJNIは必要ありません。ModがJNIに手を伸ばすのは、JVMだけでは処理できない場合、通常はグラフィックス、オーディオ、OS統合、または高速に処理する必要があるルーチンなどです。ネイティブライブラリが重い処理を行い、JNIはそのライブラリをロードし呼び出すための標準的な方法です.
実績のあるCライブラリを再利用する
既存のネイティブコードベースが既にその問題を解決しています。Javaで書き直すのは無駄な努力なので、このMODはそれにバインドします.
オペレーティングシステムに到達する
プラットフォームのいくつかの機能には、純粋なJava APIが存在しません。ネイティブ呼び出しが唯一の手段となります.
疾風の道
タイトな数値処理やローレベルなルーチンは、JVM 上でバイトコードとして実行するよりも、コンパイルされたネイティブコードとして実行する方が高速になることがある.
ドライブハードウェアとドライバー
レンダリング、サウンド、およびデバイスへのアクセスは、JNIがJava側に公開するネイティブライブラリの背後に頻繁に存在します.
JNI 呼び出しはエンドツーエンドでどのように機能しますか?
その流れはあらゆるプラットフォームで同一です。Java でメソッドを宣言し、対応する C 関数を共有ライブラリにコンパイルし、実行時にロードし、JVM に名前でリンクさせ、そしてクロス呼び出しを行います。関数名は固定パターンに従うため、JVM がそれを解決でき、生成されたヘッダーは実装すべき正確なシグネチャを記述しています。
| ステップ | 何が起こるか |
|---|---|
| 宣言 | Java メソッドに native キーワードを付与し、Java 側では空のままにします。 |
| ビルド | C または C++ 関数を共有ライブラリにコンパイルします。Windows では .dll、Linux では .so、macOS では .dylib です。 |
| ロード | Java はプログラム実行中に System.loadLibrary を呼び出してそのライブラリをロードします。 |
| リンク | JVM は各 native メソッドを名前修飾されたシグネチャによって対応する C 関数にマッチさせます。 |
| 呼び出し | Java はメソッドを呼び出し、制御がネイティブコードに渡り、結果とともに戻ります。 |
JNI はあなたにいくらかかるのですか??
JNI は強力ですが、危険な側面もあります。 ネイティブコードが JVM の安全網の外で実行されるため、そこでエラーが発生すると、キャッチ可能な例外をスローする代わりに、プロセス全体がクラッシュします。 ネイティブ側で割り当てられたメモリはガベージコレクタから見えないため、手動で解放しない限り、メモリリークが蓄積します。 境界を越えるたびにオーバーヘッドが発生し、タイトなループ内で境界を越えて呼び出すとコストがかかります。 また、ネイティブビルドはプラットフォームに依存するため、Windows の .dll は、別のコンパイルなしに Linux では読み込まれません.
それが買うもの
- 既存のCおよびC++ライブラリへの直接アクセス
- JVMでは到達できないOSおよびハードウェア機能への道
- 最も重要なルーチンでのネイティブ速度の実行
にかかる費用
- ネイティブクラッシュはJVM全体をダウンさせる可能性がある
- ガベージコレクタのバックアップなしでの手動メモリ管理
- プラットフォームごとのビルドの維持と配布
- ホットループにおける境界を越えるオーバーヘッド
JNI と Foreign Function and Memory API
JNI は当初の仕組みですが、これだけではありません。より新しい Java のリリースには、Foreign Function and Memory (FFM) API が搭載されており、これは、より少ない記述量とより厳格な安全性を確保しながらネイティブコードを呼び出すように設計されています。JNI は、どこにでも存在し、十分に理解されているため、既存のツールチェーンで依然として優勢です。
| JNI | FFM API | |
|---|---|---|
| 登場 | 黎明期からのオリジナル | 近年のリリースで追加されたモダン |
| 定型コード | 生成されたヘッダー、名前修飾された C 関数 | Java で定義されたメソッドハンドル、不要な C の補助コードなし |
| メモリ安全性 | 手動; 間違いは JVM を破損させる可能性がある | より強力なチェックを備えた境界付きセグメント |
| 採用状況 | 既存のライブラリで普遍的 | 成長中、新しいコードベース |
よくある質問
組み込み済みです。JNI は JDK と JVM と共に同梱されているため、Java 側は追加のものは不要です。ネイティブ側のコンパイルには、依然として各ターゲットプラットフォーム向けの C または C++ ツールチェーンが必要です.
いいえ。Java の部分は引き続き移植可能ですが、ネイティブライブラリは一つのオペレーティングシステムと CPU 用にコンパイルされます。クロスプラットフォームでの提供とは、個別の .dll、.so、.dylib をビルドしてバンドルすることです.
JNIがオリジナルの仕組みです。最近のJavaリリースではForeign Function and Memory APIが追加されており、より安全で、より少ない記述量で済むことを目指していますが、JNIは依然として広く利用され、よく理解されています.
JVM が直接何らかの処理を実行できない場合(GPU やオーディオバックエンドとの通信、プラットフォーム固有の API の呼び出し、またはパフォーマンスが重要なルーチンの実行など)、mod がネイティブライブラリをロードし、JNI 経由でそのライブラリに呼び出します.
JVM内部を実際に読んだ人たちによって構築されました。