复制和固定
GC 可能随时决定是否需要压缩垃圾回收的堆。 压缩涉及从物理上将对象从一个地址移至另一个地址。 这些对象可能由 JNI 局部或全局引用所引用。 为了确保压缩安全地进行,JNI 引用不会将指针指向堆。 至少使本机代码与对象移动隔离开一个间接级别。
如果本机方法需要获得对某个对象内部的直接寻址能力,那么情况将更为复杂。 通常,只要出现对较大原始数组的快速和共享访问需求,就会需要直接寻址和固定堆。 示例可能包含屏幕缓冲区。 在这些情况下,可以使用 JNI 临界区,这对程序员提出额外的要求,如这些函数的 JNI 描述中所指定的。 请参阅 JNI 规范以获取详细信息。
- GetPrimitiveArrayCritical 返回 Java™ 数组的直接堆地址,在调用相应的 ReleasePrimitiveArrayCritical 之前禁用垃圾回收。
- GetStringCritical 返回 java.lang.String 实例的直接堆地址,禁用了垃圾回收直至调用 ReleaseStringCritical。
所有其他 Get<PrimitiveType>ArrayElements 接口都会返回不受压缩影响的副本。
使用均衡的垃圾回收策略时,*Critical 形式的调用可能不会返回堆的直接指针,这可以通过 isCopy 标志反映出来。 发生此行为是由于内部存在大型数组,其中的数据可能不是连续的。 通常,当数组的存储量小于堆的 1/1000 时,会作为直接指针返回。
使用 isCopy 标志
NI 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;如果地址直接指向堆中的固定对象,那么会设置为 JNI_FALSE。
除临界函数外,J9 VM 始终返回副本。 由于固定的对象无法压缩,并且会使整理碎片变得复杂,因此复制将减轻 GC 的负载。
为避免发生泄漏,您必须:
- 使用 Get<Type>Region 和 Set<Type>Region 函数自行管理副本内存。
- 当不再需要副本时,通过调用相应的 Release<Type> 函数来确保释放由 Get<Type> 函数生成的副本。
使用方式标志
当您调用 Release<Type>ArrayElements 时,最后一个参数是方式标志。 当使用复制的数组时,方式标志用于避免不必要的复制到 Java 堆。 如果正在使用已固定的数组,那么将忽略方式标志。
对于每个 Get<Type> 调用,必须调用一次 Release<Type>,而不考虑 isCopy 参数的值。 此步骤是必需的,因为调用 Release<Type> 会删除 JNI 本地引用,否则可能会阻止垃圾回收。
- 0
- 更新 Java 堆上的数据。 释放副本所使用的空间。
- JNI_COMMIT
- 更新 Java 堆上的数据。 不释放副本所使用的空间。
- JNI_ABORT
- 请勿 更新 Java 堆上的数据。 释放副本所使用的空间。
对于 Release<Type> 调用,“0”方式标志是最安全的选项。 无论是否更改了数据副本,都将使用该副本来更新堆,并且不会发生泄漏。
要避免不得不复制回未更改的副本,请使用 JNI_ABORT 方式值。 如果更改了返回的数组,那么在使用 JNI_ABORT 方式值以“回滚”更改之前,请检查 isCopy 标志。 因为固定的 JVM 将使堆保留在除复制 JVM 以外的另一种状态,所以此步骤是必需的。
使用 isCopy 和方式标志的通用方式
以下是使用 isCopy 和方式标志的通用方式。 这将用于所有 JVM 并可确保落实更改并且不会发生泄漏。
- 不使用 isCopy 标志。 以 null 或 0 传递。
- 始终将方式标志设置为 0。
仅在优化时需要结合使用这些标志。 如果使用通用方式,那么还必须考虑同步。 请参阅 同步 (Synchronization)。