JNI 编程指南11—— JNI 调用性能优化

目录


|----------------|
| JNI 调用性能优化 |

为什么要做性能优化

  • Java 程序中,调用一个 Native 方法相比调用一个 Java 方法要耗时很多,我们应该减少 JNI 方法的调用,同时一次 JNI 调用尽量完成更多的事情。对于过于耗时的 JNI 调用,应该放到后台线程调用。
  • Native 程序要访问 Java 对象的字段或调用它们的方法时,本机代码必须调用 FindClass()、GetFieldID()、GetStaticFieldID、GetMethodID() 和 GetStaticMethodID() 等方法,返回的 ID 不会在 JVM 进程的生存期内发生变化。但是,获取字段或方法的调用有时会需要在 JVM 中完成大量工作,因为字段和方法可能是从超类中继承而来的,这会让 JVM 向上遍历类层次结构来找到它们。为了提高性能,我们可以把这些 ID 缓存起来,用内存换性能。

一、使用时缓存

Java 层:

java 复制代码
public class TestJavaClass {

    //......
    private void myMethod() {
        Log.i("JNI", "this is java myMethod");
    }
    //......
}

public native void cacheTest();

Natice 层

c 复制代码
extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_cacheTest(JNIEnv *env, jobject thiz) {

    jclass clazz = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");
    if (clazz == NULL) {
        return;
    }

    static jmethodID java_construct_method_id = NULL;
    static jmethodID java_method_id = NULL;

    //实现缓存的目的,下次调用不用再获取 methodid 了
    if (java_construct_method_id == NULL) {
        //构造函数 id
        java_construct_method_id = env->GetMethodID(clazz, "<init>", "()V");
        if (java_construct_method_id == NULL) {
            return;
        }
    }

    //调用构造函数,创建一个对象
    jobject object_test = env->NewObject(clazz, java_construct_method_id);
    if (object_test == NULL) {
        return;
    }
    //相同的手法,缓存 methodid
    if (java_method_id == NULL) {
        java_method_id = env->GetMethodID(clazz, "myMethod", "()V");
        if (java_method_id == NULL) {
            return;
        }
    }

    //调用 myMethod 方法
    env->CallVoidMethod(object_test, java_method_id);

    env->DeleteLocalRef(clazz);
    env->DeleteLocalRef(object_test);
}

手法还是比较简单的,主要是通过一个全局变量保存 methodid,这样只有第一次调用 native 函数时,才会调用 GetMethodID 去获取,后面的调用都使用缓存起来的值了。这样就避免了不必要的调用,提升了性能。

二、静态初始化缓存

Java 层:

java 复制代码
static {
    System.loadLibrary("myjnidemo");
    initIDs();
}

public static native void initIDs();

C++ 层:

c 复制代码
//定义用于缓存的全局变量
static jmethodID java_construct_method_id2 = NULL;
static jmethodID java_method_id2 = NULL;

extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_initIDs(JNIEnv *env, jclass clazz) {

    jclass clazz2 = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");

    if (clazz == NULL) {
        return;
    }

    //实现缓存的目的,下次调用不用再获取 methodid 了
    if (java_construct_method_id2 == NULL) {
        //构造函数 id
        java_construct_method_id2 = env->GetMethodID(clazz2, "<init>", "()V");
        if (java_construct_method_id2 == NULL) {
            return;
        }
    }

    if (java_method_id2 == NULL) {
        java_method_id2 = env->GetMethodID(clazz2, "myMethod", "()V");
        if (java_method_id2 == NULL) {
            return;
        }
    }
}

手法和使用时缓存是一样的,只是缓存的时机变了。如果是动态注册的 JNI 还可以在 Onload 函数中来执行缓存操作。


相关推荐
良逍Ai出海1 小时前
OpenClaw 新手最该先搞懂的 2 套命令
android·java·数据库
hindon1 小时前
一文读懂 ViewModel
android
程序员JerrySUN2 小时前
别再把 HTTPS 和 OTA 看成两回事:一篇讲透 HTTPS 协议、安全通信机制与 Mender 升级加密链路的完整文章
android·java·开发语言·深度学习·流程图
音视频牛哥2 小时前
Android平台GB28181设备接入模块架构解析、功能详解与典型应用场景分析
android·android gb28181·gb28181安卓端·gb28181对接·gb28181设备·gb28181语音广播·安卓gb28181设备对接
叁两2 小时前
前端开发如何快速上手安卓APP开发?
android
guodashen0072 小时前
在安卓端启动一个服务器接口,用于接收post请求的json数据
android·服务器·json
hindon3 小时前
一文读懂Android 中的 MVC、MVP、MVVM
android
漏刻有时3 小时前
CentOS 不定时 OOM 根治方案:PHP-FPM 进程管控 + Swap 扩容 + 全维度监控
android·centos·php
恋猫de小郭4 小时前
Android 性能迎来提升:内核引入 AutoFDO 普惠所有 15-16 设备
android·前端·flutter
CS_Zero5 小时前
Android ADB调试工具使用简记
android·adb