在 Android 开发中,JNI (Java Native Interface) 是连接 Java/Kotlin 层和 C/C++ 原生代码的桥梁。然而,JNI 调用的性能开销一直是开发者关注的痛点。本文将深入剖析 Android 8.0 引入的 @FastNative 和 @CriticalNative 优化方案,从原理到实践,帮助你榨干 JNI 的最后一丝性能。
一、JNI 性能问题的根源
1.1 普通 JNI 调用的性能瓶颈
在理解优化方案之前,我们需要先了解普通 JNI 调用为什么慢。根据 Android 官方文档,每次 JNI 调用都涉及复杂的线程状态转换和 GC 协调机制。
根据 ART 运行时的实现,这个过程的核心在于 ScopedThreadSuspension 对象:
"When a Java thread starts to execute native code, and may thus not respond promptly to suspend requests, it will normally create an object of type ScopedThreadSuspension. ScopedThreadSuspension's constructor changes state to the 'suspended' state given as an argument, logically releasing the mutator lock and promising to no longer touch Java data structures."
1.3 GC 协调机制的必要性
为什么需要这么复杂的机制?根据 Android NDK 讨论组的解释:
"The stop-the-world GC mechanism uses safe pointing (i.e. each thread periodically checks to see if it needs to suspend). Native code is considered 'safe', in that it has no way of doing anything that can confuse the GC without making a JNI call."
这意味着:
- Native 代码执行时,Java 对象可能被 GC 移动 
- 必须确保 Native 代码不持有过期的 Java 对象引用 
- 状态转换是为了让 GC 知道哪些线程是"安全"的 
二、@FastNative 原理深度解析
2.1 核心优化思想
@FastNative 在 Android 8.0 中引入,其核心思想是:跳过线程状态转换,让线程在执行 Native 代码时保持 Runnable 状态。
package dalvik.annotation.optimization;
/**
 * An ART runtime built-in optimization for "native" methods to speed up JNI transitions.
 * 
 * This has the side-effect of disabling all garbage collections while executing 
 * a fast native method.
 */
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface FastNative {}普通 JNI 调用流程:
- Java → 挂起状态转换 → Native 代码 → 恢复状态转换 → Java 
FastNative 调用流程:
- Java → Native 代码 → Java(跳过状态转换) 
"Almost every JNI call has a safepoint guard. Whenever you invoke a JNI function from a native method, a thread switches from in_native to in_vm state. A part of this transition is a safepoint check."
三、@CriticalNative:极致优化
3.1 更激进的优化
@CriticalNative 是比 @FastNative 更激进的优化,它甚至去掉了 JNIEnv 和 jclass 参数:
// 普通 JNI
private native int normalMethod(int value);
// Native 签名:
// jint Java_com_example_normalMethod(JNIEnv* env, jobject thiz, jint value)
// @FastNative
@FastNative
private native int fastMethod(int value);
// Native 签名:
// jint Java_com_example_fastMethod(JNIEnv* env, jobject thiz, jint value)
// @CriticalNative
@CriticalNative
private static native int criticalMethod(int value);
// Native 签名:
// jint Java_com_example_criticalMethod(jint value)  // ⚠️ 无 JNIEnv 和 jobject!
3.2 限制条件
官方文档明确指出 @CriticalNative 的限制:
"Methods must be static—no objects for parameters, return values, or an implicit this. Only primitive types are passed to the native method. The native method does not use the JNIEnv and jclass parameters in its function definition."
适用条件:
- 必须是 static 方法 
- 只能使用基本类型参数(int, long, float, double 等) 
- 不能访问任何 Java 对象 
- 返回值也必须是基本类型或 void 
根据官方文档,@FastNative 和 @CriticalNative 必须使用 RegisterNatives 显式注册:
// JNI_OnLoad.cpp
#include <jni.h>
#include <android/log.h>
#define LOG_TAG "NativeLib"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
extern "C" {
// 外部声明的 native 方法
extern jint Java_com_example_jnioptimization_NativeLib_normalAdd(JNIEnv*, jobject, jint, jint);
extern jint Java_com_example_jnioptimization_NativeLib_fastAdd(JNIEnv*, jobject, jint, jint);
extern jint Java_com_example_jnioptimization_NativeLib_criticalAdd(jint, jint);
extern jstring Java_com_example_jnioptimization_NativeLib_fastConcat(JNIEnv*, jobject, jstring, jstring);
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    
    // 查找类
    jclass clazz = env->FindClass("com/example/jnioptimization/NativeLib");
    if (clazz == nullptr) {
        LOGI("Failed to find class");
        return JNI_ERR;
    }
    
    // 注册方法
    JNINativeMethod methods[] = {
        {"normalAdd", "(II)I", (void*)Java_com_example_jnioptimization_NativeLib_normalAdd},
        {"fastAdd", "(II)I", (void*)Java_com_example_jnioptimization_NativeLib_fastAdd},
        {"criticalAdd", "(II)I", (void*)Java_com_example_jnioptimization_NativeLib_criticalAdd},
        {"fastConcat", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", 
         (void*)Java_com_example_jnioptimization_NativeLib_fastConcat}
    };
    
    int result = env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
    if (result != JNI_OK) {
        LOGI("Failed to register native methods");
        return JNI_ERR;
    }
    
    LOGI("Native methods registered successfully");
    return JNI_VERSION_1_6;
}
} // extern "C"
四, 死锁风险与防范
"Say thread 1 just finishes fast_jni_call_to_grab_a_lock() and is in does_some_java_work(). GC kicks in and suspends thread 1. Thread 2 now is in fast_jni_call_to_grab_a_lock() but is blocked on grabbing the native lock since it's held by thread 1. Now thread suspension can't finish since thread 2 can't be suspended since it's doing FastNative JNI."
