コピーと pin (固定)
GC はいつでも、ガーベッジ・コレクトされたヒープを圧縮する必要があると判断することがあります。 圧縮では、オブジェクトをあるアドレスから別のアドレスに物理的に移動します。 これらのオブジェクトは、JNI のローカル参照またはグローバル参照によって参照されていることがあります。 安全に圧縮が行われるようにするために、JNI 参照はヒープに対する直接のポインターではありません。 1 レベル以上の間接参照によって、ネイティブ・コードはオブジェクトの移動から分離されます。
ネイティブ・メソッドにおいて、オブジェクト内部への直接アドレス可能性が必要な場合、状況はより複雑になります。 ヒープの直接的なアドレス指定 (pin) は、通常、大規模なプリミティブ配列への高速な共有アクセスが必要な場合に必要になります。 例としては、画面バッファーなどがあります。 このような場合、JNI クリティカル・セクションを使用できます。このセクションでは、これらの機能に関する JNI の説明で指定されているように、プログラマーに対して追加の要件が課されます。 詳しくは、JNI 仕様を参照してください。
- GetPrimitiveArrayCritical は、Java™ 配列の直接ヒープ・アドレスを返し、対応する ReleasePrimitiveArrayCritical が呼び出されるまでガーベッジ・コレクションを無効にします。
- GetStringCritical は、java.lang.String インスタンスの直接ヒープ・アドレスを返し、ReleaseStringCritical が呼び出されるまでガーベッジ・コレクションを無効にします。
他のすべてのGet<PrimitiveType>ArrayElementsインターフェースは、圧縮の影響を受けないコピーを返します。
バランス・ガーベッジ・コレクション・ポリシーを使用する場合、 *Critical 形式の呼び出しでは、 isCopy フラグに反映されるヒープへの直接ポインターが返されないことがあります。 この動作は、データが順次でない可能性がある大きな配列の内部表現により起こります。 通常、ヒープの 1/1000 未満のストレージを使用した配列は直接のポインターとして返されます。
isCopy フラグの使用
JNI Get<Type>関数は、参照による受け渡し出力パラメーター(jboolean * isCopy)を指定します。これにより、呼び出し元は、特定のJNI呼び出しがコピーのアドレスを返すのか、ヒープ内の固定オブジェクトのアドレスを返すのかを判別できます。
Get<Type>関数と Release<Type>関数は、以下のようにペアで提供されます。
- GetStringChars および ReleaseStringChars
- GetStringCritical および ReleaseStringCritical
- GetStringUTFChars および ReleaseStringUTFChars
- Get<PrimitiveType>ArrayElements および Release<PrimitiveType>ArrayElements
- GetPrimitiveArrayCritical および ReleasePrimitiveArrayCritical
isCopy パラメーターとしてヌル以外のアドレスを渡すと、JNI 関数は、返されたアドレスが配列要素のコピーのアドレスである場合はそのアドレスの jboolean 値を JNI_TRUE に、返されたアドレスがヒープ内の pin (固定) されたオブジェクトを直接ポイントしている場合は JNI_FALSE に設定します。
重要な関数を除き、J9 VM は常にコピーを返します。 pin (固定) されたオブジェクトは圧縮することができず、デフラグが困難なため、コピーを行うことで GC の負担を軽減できます。
リークを防ぐためには、以下の事項を守ってください。
- Get<Type>RegionおよびSet<Type>Region機能を使用して、コピー・メモリーを自分で管理します。
- コピーが不要になった場合は、対応するRelease<Type>関数を呼び出して、Get<Type>関数によって作成されたコピーを必ず解放してください。
モード・フラグの使用
Release<Type>ArrayElementsを呼び出す場合、最後のパラメーターはモード・フラグです。 モード・フラグは、コピーされた配列を処理する際に、Java ヒープへの不要なコピーを避けるために使用されます。 モード・フラグは、pin (固定) された配列を扱う際は無視されます。
Release<Type>は、isCopyパラメーターの値に関係なく、Get<Type>呼び出しごとに1回呼び出す必要があります。 このステップが必要なのは、Release<Type>を呼び出すと、ガーベッジ・コレクションを妨げる可能性があるJNIローカル参照が削除されるためです。
- 0
- Java ヒープ上のデータを更新します。 コピーによって使用されているスペースを開放します。
- JNI_COMMIT
- Java ヒープ上のデータを更新します。 コピーによって使用されているスペースを開放しません。
- アボート JNI_ABORT
- Java ヒープ上のデータは更新 しないでください 。 コピーによって使用されているスペースを開放します。
「0」モード・フラグは、Release<Type>呼び出しの最も安全な選択です。 データのコピーが変更されてもされなくても、ヒープはコピーによって更新されるので、リークは発生しません。
変更されていないコピーをコピーし直す必要をなくすには、モード値 JNI_ABORT を使用します。 返された配列を変更する場合は、モード値 JNI_ABORT を使用して変更をロールバックする前に isCopy フラグを確認してください。 pin (固定) 側の JVM では、ヒープが、コピー側の JVM とは異なる状態になるため、この手順は必須です。
isCopy とモード・フラグの一般的な使用法
isCopy とモード・フラグの一般的な使用法を説明します。 すべての JVM と連携動作し、変更内容が確実にコミットされ、リークが発生しないようにします。
- isCopy フラグを使用しないでください。 NULL または 0 を渡してください。
- モード・フラグは必ずゼロに設定してください。
これらのフラグを複雑に使用する必要があるのは、最適化のときだけです。 一般的な方法を使用する場合でも、同期について考慮する必要があります。 同期を参照してください。