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

C++创建JVM后如何让Java直接调用可执行文件中的原生方法

问题根因

你遇到的链接错误本质是JVM默认只会检索通过System.loadLibrary加载的动态库的符号表,来匹配符合Java_<类全限定名>_<方法名>命名规则的原生函数,默认不会扫描C++主可执行文件的符号表,所以直接把实现写在主程序里会找不到对应函数。

可行解决方案

不用引入额外动态库,有两种成熟方案可以实现需求:

方案1:C++侧主动显式注册原生方法(推荐)

该方案不依赖JNI默认命名规则,稳定性更高,不需要修改编译参数。
你只需要在调用JNI_CreateJavaVM拿到JNIEnv指针后、Java代码调用对应native方法前,主动给目标Java类注册原生方法即可,示例代码如下:

// 原生方法实现,可自定义命名,不需要遵守JNI默认命名规则
void myNativeImpl(JNIEnv *env, jobject instance) {
    // 你的业务逻辑
}

// 注册方法,创建完JVM后立即调用
int registerNatives(JNIEnv* env) {
    // 替换为你自己的Java类全限定名,包名用/分隔
    jclass targetClazz = env->FindClass("com/example/SomeClass");
    if (targetClazz == nullptr) {
        return JNI_ERR;
    }
    // 方法映射表配置:Java方法名、方法签名、对应原生函数指针
    const JNINativeMethod methodMap[] = {
        {"myNativeMethod", "()V", (void*)myNativeImpl}
    };
    // 执行注册
    int res = env->RegisterNatives(targetClazz, methodMap, sizeof(methodMap)/sizeof(methodMap[0]));
    env->DeleteLocalRef(targetClazz);
    return res;
}

注意:方法签名要和Java方法的参数、返回值严格对应,比如无参数无返回值的签名是()V,入参为int返回String的签名是(I)Ljava/lang/String;。

方案2:编译时导出主可执行文件全局符号

如果你想继续使用JNI默认的命名匹配规则,不需要写注册逻辑,可以通过修改编译参数让JVM能搜到主程序的符号:

  • Linux/GCC 环境:编译链接时添加-rdynamic参数,该参数会让链接器把主程序的所有符号加入动态符号表,JVM可直接检索到符合命名规则的原生函数
  • Windows/MSVC 环境:需要在原生函数声明前添加__declspec(dllexport)标记导出符号,同时确保主可执行文件所在目录在JVM的搜索路径中

两种方案都可以完全去掉独立C动态库,不需要Java侧调用System.loadLibrary,仅用C可执行文件和Java程序两部分即可实现原生方法回调。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 07:15:04