制限されるメソッド

外部関数およびメモリー(FFM) APIの一部のメソッドは安全ではないため、制限されます。制限されたメソッドは、正しく使用しないと、JVMをクラッシュさせ、メモリーの破損につながる可能性があります(エラーは表示されません)。

アプリケーションが次のいずれかの制限付きメソッドを呼び出す場合は、ネイティブ・アクセスを有効にする必要があります:

表12-1 FFM APIの制限されたメソッド

メソッド メソッドが制限される理由

java.lang.ModuleLayer.Controller.enableNativeAccess(Module)

メソッドは、呼出し元のモジュールにネイティブ・アクセスがある場合に、指定されたモジュールのネイティブ・アクセスを有効にします。このメソッドは、制限されたメソッドを呼び出す権限を伝播するため、制限されています。

AddressLayout.withTargetLayout(MemoryLayout)

特定のターゲット・レイアウトでアドレス・レイアウトを作成したら、MemorySegment.get(AddressLayout, long)などの逆参照でそれを使用して、読取り対象のセグメントをサイズ変更できますが、これは安全ではありません。

Linker.downcallHandle(FunctionDescriptor, Linker.Option...)

Linker.downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...)

ダウンコール・メソッド・ハンドルの作成は、本質的に安全ではありません。リンカーには、指定された関数ディスクリプタが呼び出される関数と互換であることを検証する方法がありません。

通常、外部ライブラリのシンボルには、十分なシグネチャ情報(外部関数パラメータの個数や型など)が含まれていないため、リンカーが実行時にリンク要求を検証できません。クライアントがやり取りするダウンコール・メソッド・ハンドルが、無効なリンク要求によって取得されていたとき(たとえば、指定した関数ディスクリプタの引数レイアウトが多すぎた場合など)、このようなやり取りの結果は未指定となり、JVMがクラッシュする可能性があります。

Linker.upcallStub(MethodHandle, FunctionDescriptor, Arena, Linker.Option...)

ダウンコール・ハンドルの作成と同様に、リンカーは、作成する関数ポインタ(「アップコール: Javaコードを関数ポインタとして外部関数に渡す」の例のqsortコンパレータなど)が、それを渡すダウンコール(同じ例のqsortメソッド・ハンドルなど)の正しいポインタかどうかを確認できません。

MemorySegment.reinterpret(long)

MemorySegment.reinterpret(long, Arena, Consumer<MemorySegment>)

MemorySegment.reinterpret(Arena, Consumer<MemorySegment>)

これらのメソッドを使用すると、メモリーの同じ領域に新しい別名を作成することで、既存のセグメントのサイズと存続期間を変更できます。詳細は、「ポインタを返す外部関数」を参照してください。

これらのメソッドによって返されるメモリー・セグメント別名に関連付けられた空間的または時間的な境界が正しくない可能性があります。たとえば、長さが10バイトのメモリー領域が長さゼロのメモリー・セグメントを支えているとします。アプリケーションはリージョンのサイズを過大評価し、MemorySegment::reinterpretを使用して100バイトの長さのセグメントを取得する可能性があります。このために、後で領域の境界外のメモリーへの逆参照が試行される可能性があり、JVMクラッシュが発生したり、さらにはエラーが表示されずにメモリーが破損したりすることさえあります。

SymbolLookup.libraryLookup(String, Arena)

SymbolLookup.libraryLookup(Path, Arena)

ライブラリをロードすると、常にネイティブ・コードが実行される可能性があります。たとえば、Linuxでは、dlopenフックを使用して実行できます。

ネイティブ・アクセスの有効化

モジュール・パス上の特定のモジュールのネイティブ・アクセスを有効にするには、モジュール名のカンマ区切りリストを指定します:

java --enable-native-access=M1,M2,... MyApp

クラス・パスのすべてのコードに対してネイティブ・アクセスを有効にするには、次のコマンドライン・オプションを使用します:

java --enable-native-access=ALL-UNNAMED MyApp

--enable-native-accessオプションを次のように指定することもできます:

  • 環境変数JDK_JAVA_OPTIONSに設定します。「JDK_JAVA_OPTIONSランチャ環境変数の使用方法」を参照してください。
  • コマンドライン引数ファイルで指定します。javaのコマンドライン引数ファイルに関する項を参照してください。
  • Enable-Native-Access: ALL-UNNAMEDを実行可能なJARファイルのマニフェストに追加します。JARマニフェストに関する項を参照してください。
  • アプリケーションのカスタム・ランタイムを作成した場合は、jlinkコマンドで--add-optionsプラグインを使用して指定します。使用可能なプラグインのリストについては、コマンドjlink –list-pluginsを実行します。
  • コードでモジュールを動的に作成する場合は、ModuleLayer.Controller::enableNativeAccessメソッドを使用してモジュールのネイティブ・アクセスを有効にします。コードは、Module::isNativeAccessEnabledメソッドを使用して、モジュールにネイティブ・アクセスがあるかどうかを動的にチェックできます。

ネイティブ・アクセスの選択的有効化

--enable-native-access=ALL-UNNAMEDオプションは、クラス・パス上のすべてのクラスのネイティブ・アクセス制限を解除します。FFM APIを使用するJARファイルをモジュール・パスに移動することで、ネイティブ・アクセスをより選択的に有効にすることをお薦めします。これにより、クラス・パス全体ではなく、これらのJARファイルに対してのみネイティブ・アクセスを有効にできます。JARファイルをモジュール化せずに、クラス・パスからモジュール・パスに移動できます。Javaランタイムによって、ファイル名に基づく名前を持つ自動モジュールとして扱われます。自動モジュールによる増分モジュール化に関する項を参照してください。

ネイティブ・アクセス制限の影響の制御

モジュールに対してネイティブ・アクセスが有効になっていない場合、そのモジュール内のコードが制限付きメソッドをコールすることは正しくありません。--illegal-native-accessコマンドライン・オプションを次のいずれかの値に設定することで、このようなモジュールが制限付きメソッドをコールしたときの処理を指定できます:

  • allow: 制限された操作を続行できます。
  • warn: 特定のモジュールで不正なネイティブ・アクセスが初めて発生したときに、制限された操作を続行し、警告を発行できます。モジュールごとに最大1つの警告が発行されます。これは、JDK 24以降のデフォルト値です。
  • deny: 不正なネイティブ・アクセス操作ごとにIllegalCallerExceptionをスローします。これは、JDKの将来のリリースのデフォルト値になります。