
在JNI层进行性能优化和防止内存泄漏是Android NDK开发的核心挑战之一。以下是我在实践中总结的关键策略和最佳实践。
FindClass, GetObjectClass, NewObject)。env->DeleteLocalRef(localRef)):env->NewGlobalRef(obj)env->DeleteGlobalRef(globalRef))。NewGlobalRef和DeleteGlobalRef。忘记删除是严重的内存泄漏源。env->NewWeakGlobalRef(obj)NULL。env->IsSameObject(weakGlobalRef, NULL) == JNI_TRUE 或 env->IsSameObject(weakGlobalRef, ...)。env->DeleteWeakGlobalRef(weakGlobalRef)。FindClass, GetMethodID, GetFieldID 涉及查找和验证,开销大。JNI_OnLoad或首次使用类的Native方法中查找并缓存为全局引用。pthread_once或C++11的std::call_once确保只初始化一次。static jclass gMyClass = nullptr;
static jmethodID gMyMethodID = nullptr;
JNIEXPORT jint 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 localMyClass = env->FindClass("com/example/MyClass");
    if (!localMyClass) return JNI_ERR;
    gMyClass = static_cast<jclass>(env->NewGlobalRef(localMyClass)); // 提升为全局引用
    env->DeleteLocalRef(localMyClass); // 删除不再需要的局部引用
    gMyMethodID = env->GetMethodID(gMyClass, "myMethod", "()V");
    if (!gMyMethodID) return JNI_ERR;
    return JNI_VERSION_1_6;
}
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return;
    if (gMyClass) {
        env->DeleteGlobalRef(gMyClass);
        gMyClass = nullptr;
    }
    // jmethodID/jfieldID 本身是普通指针,不需要Delete,但关联的jclass需要
}JNI_OnUnload等)。std::unique_ptr, std::shared_ptr 结合自定义删除器(如free, delete[], 特定库的释放函数)能极大减少手动内存管理错误。long nativePtr),在finalize()或nativeDestroy()方法中同步释放Native资源,并将指针置null。确保Native代码检查指针有效性。Get<PrimitiveType>ArrayElements/Release<PrimitiveType>ArrayElementsJNI_ABORT不写回,JNI_COMMIT写回但不释放)。Get可能复制整个数组!Release是必须的。GetPrimitiveArrayCritical/ReleasePrimitiveArrayCriticalenv->NewDirectByteBuffer(nativeAddress, capacity)ByteBuffer直接操作Native内存。Cleaner(通过java.nio.DirectByteBuffer的构造函数或sun.misc.Unsafe)在GC时触发Native释放回调(PhantomReference + ReferenceQueue)。GetStringChars / ReleaseStringChars (UTF-16) 或 GetStringUTFChars / ReleaseStringUTFChars (Modified UTF-8)。GetStringRegion / GetStringUTFRegion 复制部分字符串到预分配缓冲区,避免潜在复制和释放操作。JavaVM*(保存在JNI_OnLoad)调用 AttachCurrentThread / AttachCurrentThreadAsDaemon 获取JNIEnv*。DetachCurrentThread()。忘记Detach是常见泄漏,导致线程资源无法释放,关联的JavaThread对象泄漏。pthread_key_create + pthread_setspecific + pthread_getspecific 或 C++11 thread_local 存储JNIEnv*(确保只attach一次)。class ScopedJniEnv {
public:
    ScopedJniEnv(JavaVM* jvm) : m_jvm(jvm), m_env(nullptr), m_attached(false) {
        jint ret = m_jvm->GetEnv(reinterpret_cast<void**>(&m_env), JNI_VERSION_1_6);
        if (ret == JNI_EDETACHED) {
            ret = m_jvm->AttachCurrentThread(&m_env, nullptr);
            if (ret == JNI_OK) m_attached = true;
        }
    }
    ~ScopedJniEnv() {
        if (m_attached && m_jvm) {
            m_jvm->DetachCurrentThread();
        }
    }
    JNIEnv* operator->() { return m_env; }
    operator JNIEnv*() { return m_env; }
private:
    JavaVM* m_jvm;
    JNIEnv* m_env;
    bool m_attached;
};Call<Type>Method、创建对象NewObject、访问字段/方法ID),立即检查异常:env->CallVoidMethod(obj, methodID, ...);
if (env->ExceptionCheck()) {
    env->ExceptionDescribe(); // 打印日志(调试用)
    env->ExceptionClear();    // 清除异常(如果打算在Native处理)
    // 或者 return 让异常传播到Java层
}env->ThrowNew(env->FindClass("java/lang/Exception"), "Error message")。Get/ReleaseArrayElements。Critical规则:Get/ReleasePrimitiveArrayCritical。GetArrayRegion/SetArrayRegion。build.gradle中启用:android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared"
                cFlags "-fsanitize=address -fno-omit-frame-pointer"
                cppFlags "-fsanitize=address -fno-omit-frame-pointer"
            }
        }
        packagingOptions {
            doNotStrip "**/*.so"
        }
    }
}libc malloc/free)。ScopedJniEnv)。FindClass, GetMethodID, NewGlobalRef等,失败返回NULL或抛出异常。isDestroyed())。NewGlobalRef/DeleteGlobalRef、NewWeakGlobalRef/DeleteWeakGlobalRef、AttachCurrentThread/DetachCurrentThread、Get<Type>ArrayElements/Release<Type>ArrayElements、GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical、NewDirectByteBuffer关联的释放机制、Native指针在Java侧的释放点。assert(ptr != nullptr))。核心原则:JNI层的内存泄漏往往源于对JVM引用生命周期管理的疏忽(尤其是全局引用和线程附着)以及Native内存与Java对象生命周期的不同步。严格遵循引用管理规则、善用现代C++特性(智能指针、RAII)、充分利用强大的工具(ASan, Profiler, Simpleperf)进行检测和分析,是构建高性能、无泄漏JNI代码的关键。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。