深入浅出 Android JNI (五):性能优化

0 阅读5分钟

1. JNI 调用开销分析

1.1 JNI 调用的隐藏成本

操作类型相对开销原因分析
Java ↔ Native 调用跨越 VM 边界,上下文切换
获取字段/方法 ID非常高运行时查找,类似反射
创建 Java 对象触发 GC 可能性
基本类型传递极低直接值拷贝
引用类型转换中-高内存复制/锁定

1.2 性能优化黄金法则

  1. 减少跨界调用次数:批量处理数据
  2. 缓存重复使用资源:ID、类引用等
  3. 选择高效数据通道:直接缓冲区
  4. 避免不必要转换:最小化数据复制

2. 高效数据传递策略

2.1 数组操作优化

传统方式 (高开销):

jint sum = 0;
for (int i = 0; i < len; i++) {
    jint element = env->GetIntArrayElements(array, nullptr)[i];
    sum += element;
    env->ReleaseIntArrayElements(array, data, JNI_ABORT);
}

优化方案 (批处理):

jint* data = env->GetIntArrayElements(array, nullptr);
jint sum = 0;
for (int i = 0; i < len; i++) {
    sum += data[i]; // 单次锁定,多次访问
}
env->ReleaseIntArrayElements(array, data, JNI_ABORT);

临界区访问 (零拷贝):

// 仅当不需要修改数据时使用
const jint* data = env->GetPrimitiveArrayCritical(array, nullptr);
jint sum = 0;
for (int i = 0; i < len; i++) {
    sum += data[i];
}
env->ReleasePrimitiveArrayCritical(array, const_cast<jint*>(data), JNI_ABORT);

2.2 使用直接缓冲区 (NIO Buffer)

Java 侧:

// 创建直接缓冲区 (Native 内存)
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);

// 传递到 Native
nativeProcessBuffer(buffer);

C++ 侧:

extern "C" void JNICALL
Java_com_example_NativeLib_processBuffer(JNIEnv* env, jobject, jobject buffer) {
    // 获取直接内存地址
    uint8_t* data = static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
    jlong capacity = env->GetDirectBufferCapacity(buffer);
    
    // 直接操作内存 (零拷贝)
    process_image(data, capacity);
    
    // 注意:不需要释放!
}

优势:

  • 避免 Java 堆与 Native 堆间的数据复制
  • 支持大规模数据处理 (视频/音频/图像)
  • 内存由 Java 管理,自动回收

3. 引用与 ID 缓存策略

3.1 方法/字段 ID 缓存

全局静态缓存 (推荐):

static jclass g_bitmapClass = nullptr;
static jmethodID g_createBitmap = nullptr;

JNIEXPORT void JNICALL
Java_com_example_ImageUtils_init(JNIEnv* env, jclass) {
    if (g_bitmapClass == nullptr) {
        jclass local = env->FindClass("android/graphics/Bitmap");
        g_bitmapClass = static_cast<jclass>(env->NewGlobalRef(local));
        env->DeleteLocalRef(local);
    }
    
    if (g_createBitmap == nullptr) {
        g_createBitmap = env->GetStaticMethodID(g_bitmapClass, "createBitmap", 
            "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
    }
}

3.2 类引用缓存

初始化时缓存:

// 在 JNI_OnLoad 中全局缓存
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
    
    jclass local = env->FindClass("java/nio/ByteBuffer");
    g_byteBufferClass = static_cast<jclass>(env->NewGlobalRef(local));
    env->DeleteLocalRef(local);
    
    return JNI_VERSION_1_6;
}

4. JNI 注册优化

4.1 静态注册 vs 动态注册

特性静态注册动态注册
注册方式自动 (按命名规则)手动 (JNI_OnLoad)
性能首次调用慢 (按需查找)启动时注册 (无查找开销)
灵活性低 (函数名固定)高 (可任意映射)
混淆影响需保持函数名无影响
维护成本低 (IDE 友好)高 (手动维护注册表)

4.2 动态注册示例

// Native 方法实现
void native_method1(JNIEnv* env, jobject) { /* ... */ }
jint native_method2(JNIEnv* env, jobject, jint param) { /* ... */ }

// 方法映射表
static JNINativeMethod methods[] = {
    {"nativeMethod1", "()V", reinterpret_cast<void*>(native_method1)},
    {"nativeMethod2", "(I)I", reinterpret_cast<void*>(native_method2)}
};

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
    
    // 注册类方法
    jclass clazz = env->FindClass("com/example/NativeClass");
    env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod));
    env->DeleteLocalRef(clazz);
    
    return JNI_VERSION_1_6;
}

5. 内存管理高级技巧

5.1 局部引用表优化

问题代码 (可能导致溢出):

void process_objects(JNIEnv* env, jobjectArray array) {
    jsize len = env->GetArrayLength(array);
    for (int i = 0; i < len; i++) {
        jobject obj = env->GetObjectArrayElement(array, i);
        // 处理对象...
        // 忘记删除局部引用!
    }
}

优化方案:使用局部帧

void process_objects(JNIEnv* env, jobjectArray array) {
    jsize len = env->GetArrayLength(array);
    for (int i = 0; i < len; i++) {
        env->PushLocalFrame(10); // 创建新局部帧
        
        jobject obj = env->GetObjectArrayElement(array, i);
        // 处理对象...
        
        env->PopLocalFrame(nullptr); // 释放当前帧所有局部引用
    }
}

5.2 弱引用智能缓存

// 全局弱引用缓存
static jweak g_cachedObject = nullptr;

jobject get_cached_object(JNIEnv* env) {
    if (g_cachedObject != nullptr) {
        jobject localRef = env->NewLocalRef(g_cachedObject);
        if (localRef != nullptr) {
            return localRef; // 对象仍存在
        }
        // 对象已被GC,清除弱引用
        env->DeleteWeakGlobalRef(g_cachedObject);
        g_cachedObject = nullptr;
    }
    
    // 创建新对象并缓存弱引用
    jobject newObj = create_new_object(env);
    g_cachedObject = env->NewWeakGlobalRef(newObj);
    return env->NewLocalRef(newObj);
}

6. 线程池与异步处理

6.1 Native 线程池集成

#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

class ThreadPool {
public:
    ThreadPool(size_t threads, JavaVM* jvm) : vm(jvm) {
        for(size_t i = 0; i < threads; ++i)
            workers.emplace_back([this] { worker_loop(); });
    }
    
    void enqueue(std::function<void(JNIEnv*)> task) {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            tasks.emplace(std::move(task));
        }
        condition.notify_one();
    }
    
    ~ThreadPool() {
        stop = true;
        condition.notify_all();
        for(std::thread &worker: workers)
            worker.join();
    }

private:
    JavaVM* vm;
    std::vector<std::thread> workers;
    std::queue<std::function<void(JNIEnv*)>> tasks;
    
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop = false;
    
    void worker_loop() {
        JNIEnv* env;
        vm->AttachCurrentThread(&env, nullptr);
        
        while(!stop) {
            std::function<void(JNIEnv*)> task;
            {
                std::unique_lock<std::mutex> lock(queue_mutex);
                condition.wait(lock, [this]{ return stop || !tasks.empty(); });
                if(stop && tasks.empty()) break;
                task = std::move(tasks.front());
                tasks.pop();
            }
            task(env);
        }
        vm->DetachCurrentThread();
    }
};

// 全局线程池
static ThreadPool* g_pool = nullptr;

// 初始化
extern "C" JNIEXPORT void JNICALL
Java_com_example_NativeLib_initThreadPool(JNIEnv* env, jclass, jint size) {
    JavaVM* vm;
    env->GetJavaVM(&vm);
    g_pool = new ThreadPool(size, vm);
}

// 提交任务
extern "C" JNIEXPORT void JNICALL
Java_com_example_NativeLib_submitTask(JNIEnv* env, jobject, jlong param) {
    g_pool->enqueue([param](JNIEnv* env) {
        process_task(env, param);
    });
}

7. 高级技巧:混合 Java/Native 对象

7.1 在 Native 中保存 Java 对象

class NativeObjectWrapper {
public:
    NativeObjectWrapper(JNIEnv* env, jobject obj) 
        : vm(nullptr), java_ref(nullptr) 
    {
        env->GetJavaVM(&vm);
        java_ref = env->NewGlobalRef(obj);
    }
    
    virtual ~NativeObjectWrapper() {
        if (vm && java_ref) {
            JNIEnv* env = get_env();
            env->DeleteGlobalRef(java_ref);
        }
    }
    
    void call_java_method() {
        JNIEnv* env = get_env();
        jclass cls = env->GetObjectClass(java_ref);
        jmethodID mid = env->GetMethodID(cls, "callback", "()V");
        env->CallVoidMethod(java_ref, mid);
        env->DeleteLocalRef(cls);
    }
    
private:
    JavaVM* vm;
    jobject java_ref;
    
    JNIEnv* get_env() {
        JNIEnv* env = nullptr;
        vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
        return env;
    }
};

// 从 Java 创建 Native 对象
extern "C" JNIEXPORT jlong JNICALL
Java_com_example_NativeObject_createNative(JNIEnv* env, jobject obj) {
    auto* wrapper = new NativeObjectWrapper(env, obj);
    return reinterpret_cast<jlong>(wrapper);
}

// 调用 Native 对象方法
extern "C" JNIEXPORT void JNICALL
Java_com_example_NativeObject_callNative(JNIEnv* env, jobject, jlong ptr) {
    auto* wrapper = reinterpret_cast<NativeObjectWrapper*>(ptr);
    wrapper->call_java_method();
}

// 销毁 Native 对象
extern "C" JNIEXPORT void JNICALL
Java_com_example_NativeObject_destroyNative(JNIEnv* env, jobject, jlong ptr) {
    delete reinterpret_cast<NativeObjectWrapper*>(ptr);
}