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

如何通过JNI从C++调用带Functional Interface参数的Java方法?

解决JNI调用带Functional Interface参数Java方法的段错误问题

问题描述

需要通过JNI从C调用带有Consumer<String>参数的Java方法,实现Java向C的回调,但运行编译脚本后出现SIGSEGV段错误。

原有代码

Main.java

import java.util.function.Consumer;

public class Main {
  static {
    System.loadLibrary("Main");
  }

  public static void main(String[] args) {
    Main m = new Main();
    m.nativeMethod();
  }

  public void test() {
      System.out.println("test");
  }

  public void getCallback(Consumer<String> consumer) {
        consumer.accept("Data from Java to cpp");
  }

  public native void nativeMethod();
}

Main.cpp

#include <iostream>
#include <string>
#include "Main.h"

extern "C"
{

    JNIEXPORT void JNICALL myCallback(JNIEnv *env, jobject obj, jstring data)
    {
        jboolean isCopy;
        const char *cData = env->GetStringUTFChars(data, &isCopy);
        std::string cppData(cData);
        env->ReleaseStringUTFChars(data, cData);

        std::cout << cppData << '\n';
    }

    JNIEXPORT void JNICALL Java_Main_nativeMethod(JNIEnv *env, jobject obj)
    {

        std::cout << "Hello from Java_Main_nativeMethod" << '\n';

        jmethodID jTest = env->GetMethodID(
            env->GetObjectClass(obj),
            "test",
            "()V");
        env->CallVoidMethod(obj, jTest);

        jmethodID jFunction = env->GetMethodID(
            env->GetObjectClass(obj),
            "getCallback",
            "(Ljava/util/function/Consumer;)V");
        env->CallVoidMethod(obj, jFunction, myCallback);
    }
}

compile.bash

#!/bin/bash
cd "$(dirname "$0")"

javac -h . Main.java  # generates header
javac Main.java
gcc -lstdc++ -shared -I /Library/Java/JavaVirtualMachines/temurin-20.jdk/Contents/Home/include -o libMain.jnilib Main.cpp
java Main

错误输出

Hello from Java_Main_nativeMethod
test
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000000108554b14, pid=72842, tid=10499
#
# JRE version: OpenJDK Runtime Environment Temurin-20.0.2+9 (20.0.2+9) (build 20.0.2+9)
# Java VM: OpenJDK 64-Bit Server VM Temurin-20.0.2+9 (20.0.2+9, mixed mode, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)
# Problematic frame:
# V  [libjvm.dylib+0x514b14]  jni_CallVoidMethodV+0xe0
#
# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# hs_err_pid72842.log
#
# If you would like to submit a bug report, please visit:
#   https://github.com/adoptium/adoptium-support/issues
#
zsh: abort      java Main

问题根源

直接将C函数指针myCallback作为参数传给Java的Consumer类型参数是错误的。Java的Functional Interface要求传入的是实现该接口的Java对象,而原生函数指针是C内存地址,Java虚拟机无法识别,导致内存访问错误。

修改方案

通过Java层封装回调逻辑,创建实现Consumer<String>的对象,该对象的accept方法调用原生静态方法,再在C++中获取这个Java对象并传给getCallback方法。

修改后的代码

Main.java

import java.util.function.Consumer;

public class Main {
  static {
    System.loadLibrary("Main");
  }

  public static void main(String[] args) {
    Main m = new Main();
    m.nativeMethod();
  }

  public void test() {
      System.out.println("test");
  }

  public void getCallback(Consumer<String> consumer) {
        consumer.accept("Data from Java to cpp");
  }

  // 静态原生方法,作为Java回调C++的入口
  public static native void onCallback(String data);

  // 返回绑定了原生方法的Consumer对象
  public Consumer<String> getConsumer() {
      return Main::onCallback;
  }

  public native void nativeMethod();
}

Main.cpp

#include <iostream>
#include <string>
#include "Main.h"

extern "C"
{
    // 实现静态原生回调方法
    JNIEXPORT void JNICALL Java_Main_onCallback(JNIEnv *env, jclass clazz, jstring data)
    {
        jboolean isCopy;
        const char *cData = env->GetStringUTFChars(data, &isCopy);
        std::string cppData(cData);
        env->ReleaseStringUTFChars(data, cData);

        std::cout << cppData << '\n';
    }

    JNIEXPORT void JNICALL Java_Main_nativeMethod(JNIEnv *env, jobject obj)
    {
        std::cout << "Hello from Java_Main_nativeMethod" << '\n';

        // 调用test方法
        jmethodID jTest = env->GetMethodID(
            env->GetObjectClass(obj),
            "test",
            "()V");
        env->CallVoidMethod(obj, jTest);

        // 获取getConsumer方法ID,调用得到Consumer对象
        jmethodID getConsumerMethod = env->GetMethodID(
            env->GetObjectClass(obj),
            "getConsumer",
            "()Ljava/util/function/Consumer;");
        jobject consumerObj = env->CallObjectMethod(obj, getConsumerMethod);

        // 获取getCallback方法ID,传入Consumer对象调用
        jmethodID getCallbackMethod = env->GetMethodID(
            env->GetObjectClass(obj),
            "getCallback",
            "(Ljava/util/function/Consumer;)V");
        env->CallVoidMethod(obj, getCallbackMethod, consumerObj);

        // 释放局部引用(规范操作)
        env->DeleteLocalRef(consumerObj);
    }
}

运行结果

执行编译脚本后,将正常输出:

Hello from Java_Main_nativeMethod
test
Data from Java to cpp

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 20:08:17