You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JNI返回Java对象引发GC异常导致应用崩溃问题咨询

问题核心原因

Java的finalize机制存在一个关键特性:一旦对象被GC标记为可回收并加入finalizer队列,即使后续对象重新变为可达,finalizer线程仍会执行其finalize方法。结合你的场景:

  • GC提前启动,标记了缓存中的Java对象为可回收,将其加入finalizer队列,但此时finalizer线程还没来得及处理;
  • Java侧调用C++的getObj时,通过env->NewLocalRef(weak_obj)让对象重新可达,但此时finalizer队列已经锁定该对象待处理;
  • 后续finalizer线程执行对象的finalize方法(对应C++侧的资源销毁逻辑),导致对象内部状态失效,此时Java侧仍持有引用,调用methodB就会访问已销毁的资源,触发崩溃。
可行解决方案

方案1:给对象添加跳过finalize的标记

在Java对象中增加一个开关,当从C++缓存中取回对象时,标记它不需要执行finalize逻辑,避免资源被销毁:
Java代码:

public class TargetObject {
    private boolean skipFinalize = false;

    @Override
    protected void finalize() throws Throwable {
        if (!skipFinalize) {
            // 原有的C++侧销毁逻辑调用
            nativeDestroy();
        }
        super.finalize();
    }

    // 供C++调用,标记对象无需执行finalize
    public void markReused() {
        skipFinalize = true;
    }
}

C++代码:

jobject getObj(JNIEnv* env, jweak weakObj) {
    jobject localRef = env->NewLocalRef(weakObj);
    if (localRef != nullptr) {
        jclass objCls = env->GetObjectClass(localRef);
        jmethodID markMethod = env->GetMethodID(objCls, "markReused", "()V");
        env->CallVoidMethod(localRef, markMethod);
        env->DeleteLocalRef(objCls);
    }
    return localRef;
}

方案2:替换jweak为强引用+显式销毁

放弃依赖jweak和finalize,改用全局强引用缓存,由Java侧显式通知C释放资源,彻底避免GC和finalizer的冲突:
C
侧缓存逻辑:

#include <unordered_map>
#include <string>

std::unordered_map<std::string, jobject> globalObjCache;

// 缓存Java对象,创建全局强引用
void cacheObject(JNIEnv* env, const std::string& key, jobject obj) {
    auto iter = globalObjCache.find(key);
    if (iter != globalObjCache.end()) {
        env->DeleteGlobalRef(iter->second);
    }
    globalObjCache[key] = env->NewGlobalRef(obj);
}

// 获取对象,返回本地引用
jobject getObject(JNIEnv* env, const std::string& key) {
    auto iter = globalObjCache.find(key);
    if (iter != globalObjCache.end()) {
        return env->NewLocalRef(iter->second);
    }
    return nullptr;
}

// 显式销毁对象,释放全局引用
void destroyObject(JNIEnv* env, const std::string& key) {
    auto iter = globalObjCache.find(key);
    if (iter != globalObjCache.end()) {
        env->DeleteGlobalRef(iter->second);
        globalObjCache.erase(iter);
    }
}

Java侧调用逻辑:

public class UIManager {
    // 使用对象后主动调用销毁
    public void finishUsingObject(String key) {
        nativeDestroyObject(key);
    }
}

方案3:临时提升为全局引用直到使用完毕

在C侧取回对象时,临时创建全局引用,确保对象不会被GC再次标记,直到Java侧使用完毕再释放:
C
代码:

#include <unordered_map>
#include <string>

std::unordered_map<std::string, jobject> tempGlobalRefs;

jobject getObj(JNIEnv* env, const std::string& key, jweak weakObj) {
    jobject localRef = env->NewLocalRef(weakObj);
    if (localRef != nullptr) {
        // 创建临时全局引用,防止GC回收
        jobject globalRef = env->NewGlobalRef(localRef);
        tempGlobalRefs[key] = globalRef;
    }
    return localRef;
}

// Java侧使用完毕后调用,释放临时全局引用
void releaseTempRef(JNIEnv* env, const std::string& key) {
    auto iter = tempGlobalRefs.find(key);
    if (iter != tempGlobalRefs.end()) {
        env->DeleteGlobalRef(iter->second);
        tempGlobalRefs.erase(iter);
    }
}

Java侧使用示例:

public void processObject(String key) {
    TargetObject obj = nativeGetObj(key);
    try {
        obj.methodA();
        // 业务逻辑执行
        obj.methodB();
    } finally {
        // 确保释放临时引用
        nativeReleaseTempRef(key);
    }
}

内容的提问来源于stack exchange,提问作者comrade_bender

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:08:16