美文网首页
★08.缓存属性和方法ID

★08.缓存属性和方法ID

作者: iDragonfly | 来源:发表于2017-07-03 19:43 被阅读0次

简介

  • 在获取一个 属性/方法ID 的时候需要基于名称或者 属性/方法描述符符号查找符号查找 的代价相对来说是比较昂贵的(消耗时间和资源),优化主要思路的是计算 属性/方法ID 通过缓存以供后续使用。
  • 有两种缓存方式,如有可能应该尽量使用后者: 用时缓存类静态初始化器缓存

用时缓存

简单示例

JNIEXPORT void JNICALL Java_InstanceFieldAccess_accessField(JNIEnv * env, jobject obj) {
    // 静态局部变量以便于一次获取,以后就不必重新获取
    static jfieldID fid_s = NULL;

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

    // 只有从来没获取fid_s才会获取
    if (!fid_s) {
        fid_s = (* env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
        if (!fid_s) {
            return;
        }
    }

    jstring jstr = (* env)->GetObjectField(env, obj, fid_s);
    const char * str = (* env)->GetStringUTFChars(env, jstr, NULL);
    if (!str) {
        return;
    }
    printf("In C: \n");
    printf("c.s = \"%s\"\n", str);
    (* env)->ReleaseStringUTFChars(env, jstr, str);
    jstring newJstr = (* env)->NewStringUTF(env, "123");
    if (!newJstr) {
        return;
    }
    (* env)->SetObjectField(env, obj, fid_s, newJstr);
}

示例解说

  • 示例中的属性IDfid_s使用了静态局部存储,用以缓存,重复使用时不再重新获取。

优缺点

  • 优点 :非侵入性的,即在对 Java 代码没有控制权的时候,仍然可以用 用时缓存 ,而 类静态初始化器缓存 的方法则不可以。
  • 缺点
    • 在多线程的情况下,可能会出现fid_s重复计算/缓存/检查 的问题。在此处fid_s重复计算/缓存 除了有一部分性能开销以外,基本是无害的。
    • 当类被载出时,缓存的ID不再有效,需要获取,在使用 用时缓存 的方法时,是需要保证当原生代码仍然在用缓存ID时,类不会被载出或重新加载。

类静态初始化器缓存

简单示例

public class InstanceMethodCall {
    static {
        System.loadLibrary("InstanceMethodCall");
        // 在类静态初始化代码块中缓存ID
        initIDs();
    }

    public static void main(String args[]) {
        InstanceMethodCall c = new InstanceMethodCall();
        c.nativeMethod();
    }

    // 类静态函数,实现交给原生代码,用于缓存ID
    private static native void initIDs();

    private native void nativeMethod();

    private void callback() {
        System.out.println("In Java");
    }
}
// 创建全局变量,以便于ID可以在多个不同的原生函数中传递
jmethodID MID_InstanceMethodCall_callback;

JNIEXPORT void JNICALL Java_InstanceMethodCall_initIDs(JNIEnv * env, jclass cls) {
    // 获取所有需要缓存的ID到全局变量中
    MID_InstanceMethodCall_callback = (* env)->GetMethodID(env, cls, "callback", "()V");
}

JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv * env, jobject obj) {
    printf("In C\n");
    // 使用缓存的ID
    (* env)->CallVoidMethod(env, obj, MID_InstanceMethodCall_callback);
}

示例解说

  • Java 代码中声明一个静态native函数initIDs()用于缓存ID。
  • Java 代码中 类静态构造代码块 中调用initIDs()
  • 在原生代码中实现initIDs(),缓存ID到全局变量。

优缺点

  • 优点 :不需要手动获取缓存ID,当类载入或重新加载时,ID会自动缓存。
  • 缺点 :侵入性,无法在对 Java 代码没有控制权的情况下使用。

两种方法的性能比较

概念

  • Java/native调用 :原生代码调用 Java 函数。
  • native/Java调用Java 代码调用原生函数。
  • Java/Java调用Java 代码调用 Java 函数。

Java/native调用

  • Java/native调用 可能慢于 Java/Java调用 ,原因是:
    • 不得不执行额外的操作。
    • 原生代码调用 Java 函数难以内联。
  • 经典的虚拟机执行 Java/native调用 会2-3倍慢于 Java/Java调用 ,但是构建一个虚拟机使得二者的性能开销接近甚至相等也是可能的。

native/Java调用

  • 理论上 native/Java调用 相比 Java/Java调用 会慢2-3倍,但是 native/Java调用 比较罕见。

相关文章

网友评论

      本文标题:★08.缓存属性和方法ID

      本文链接:https://www.haomeiwen.com/subject/msuzcxtx.html