技术开发 频道

Java JNI深度分析与实践

  缓存会对您应用程序的运行时造成显著的影响。考虑下面两个版本的方法,它们的作用是相同的。清单 2 使用了缓存的字段 ID:

  清单 2. 使用缓存的字段 ID

  int sumValues2(JNIEnv* env, jobject obj, jobject allValues){

  jint avalue = (*env)->GetIntField(env, allValues, a);

  jint bvalue = (*env)->GetIntField(env, allValues, b);

  jint cvalue = (*env)->GetIntField(env, allValues, c);

  jint dvalue = (*env)->GetIntField(env, allValues, d);

  jint evalue = (*env)->GetIntField(env, allValues, e);

  jint fvalue = (*env)->GetIntField(env, allValues, f);

  return avalue + bvalue + cvalue + dvalue + evalue + fvalue;

  }

  清单 3. 未缓存字段 ID

  int sumValues2(JNIEnv* env, jobject obj, jobject allValues){

  jclass cls = (*env)->GetObjectClass(env,allValues);

  jfieldID a = (*env)->GetFieldID(env, cls, "a", "I");

  jfieldID b = (*env)->GetFieldID(env, cls, "b", "I");

  jfieldID c = (*env)->GetFieldID(env, cls, "c", "I");

  jfieldID d = (*env)->GetFieldID(env, cls, "d", "I");

  jfieldID e = (*env)->GetFieldID(env, cls, "e", "I");

  jfieldID f = (*env)->GetFieldID(env, cls, "f", "I");

  jint avalue = (*env)->GetIntField(env, allValues, a);

  jint bvalue = (*env)->GetIntField(env, allValues, b);

  jint cvalue = (*env)->GetIntField(env, allValues, c);

  jint dvalue = (*env)->GetIntField(env, allValues, d);

  jint evalue = (*env)->GetIntField(env, allValues, e);

  jint fvalue = (*env)->GetIntField(env, allValues, f);

  return avalue + bvalue + cvalue + dvalue + evalue + fvalue

  清单 2 用 3,572 ms 运行了 10,000,000 次。清单 3 用了 86,217 ms — 多花了 24 倍的时间。

  触发数组副本

  JNI 在 Java 代码和本机代码之间提供了一个干净的接口。为了维持这种分离,数组将作为不透明的句柄传递,并且本机代码必须回调 JVM 以便使用 set 和 get 调用操作数组元素。Java 规范让 JVM 实现决定让这些调用提供对数组的直接访问,还是返回一个数组副本。举例来说,当数组经过优化而不需要连续存储时,JVM 可以返回一个副本。(参见 参考资料 获取关于 JVM 的信息)。

  随后,这些调用可以复制被操作的元素。举例来说,如果您对含有 1,000 个元素的数组调用 GetLongArrayElements(),则会造成至少分配或复制 8,000 字节的数据(每个 long 1,000 元素 * 8 字节)。当您随后使用 ReleaseLongArrayElements() 更新数组的内容时,需要另外复制 8,000 字节的数据来更新数组。即使您使用较新的 GetPrimitiveArrayCritical(),规范仍然准许 JVM 创建完整数组的副本。

  GetTypeArrayRegion() 和 SetTypeArrayRegion() 方法允许您获取和更新数组的一部分,而不是整个数组。通过使用这些方法访问较大的数组,您可以确保只复制本机代码将要实际使用的数组部分。

  举例来说,考虑相同方法的两个版本,如清单 4 所示:

  清单 4. 相同方法的两个版本

  jlong getElement(JNIEnv* env, jobject obj, jlongArray arr_j,

  int element){

  jboolean isCopy;

  jlong result;

  jlong* buffer_j = (*env)->GetLongArrayElements(env, arr_j, &isCopy);

  result = buffer_j[element];

  (*env)->ReleaseLongArrayElements(env, arr_j, buffer_j, 0);

  return result;

  }

  jlong getElement2(JNIEnv* env, jobject obj, jlongArray arr_j,

  int element){

  jlong result;

  (*env)->GetLongArrayRegion(env, arr_j, element,1, &result);

  return result;

  }

  }

0
相关文章