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

如何从Java本地接口(JNI)调用getStackTrace方法

通过JNI获取Java堆栈跟踪的实现示例

我最近在啃JNI的相关知识,想着试试用它来获取Java的堆栈跟踪,先给大家看看我写的Java端代码:

package test.com.jni;

public class HelloJNI {
    static {
        System.loadLibrary("hello"); // 运行时加载本地库
    }

    // 声明native方法:获取堆栈跟踪数组
    private native StackTraceElement[] getStackTraceNative(Throwable throwable);
    // 声明native方法:打印堆栈跟踪
    private native void printStackTraceNative(Throwable throwable);

    public static void main(String[] args) {
        test();
    }

    public static void test() {
        // 实例化并调用native方法测试
        new HelloJNI().printStackTraceNative(new Throwable("测试异常"));
    }
}

接下来是对应的C语言实现(文件名hello.c),这里实现了两个native方法的逻辑,核心是通过JNI调用Java中Throwable类的getStackTrace()方法来获取堆栈信息:

#include <jni.h>
#include <stdio.h>

// 获取JNI环境中对应的类和方法ID
JNIEXPORT jobjectArray JNICALL Java_test_com_jni_HelloJNI_getStackTraceNative
  (JNIEnv *env, jobject obj, jobject throwable) {
    // 获取Throwable类
    jclass throwableClass = (*env)->GetObjectClass(env, throwable);
    // 获取getStackTrace方法的ID:方法签名是()[Ljava/lang/StackTraceElement;
    jmethodID getStackTraceMethod = (*env)->GetMethodID(env, throwableClass, "getStackTrace", "()[Ljava/lang/StackTraceElement;");
    // 调用getStackTrace方法,返回StackTraceElement数组
    jobjectArray stackTraceArray = (jobjectArray)(*env)->CallObjectMethod(env, throwable, getStackTraceMethod);
    
    return stackTraceArray;
}

JNIEXPORT void JNICALL Java_test_com_jni_HelloJNI_printStackTraceNative
  (JNIEnv *env, jobject obj, jobject throwable) {
    // 先调用上面的方法获取堆栈数组
    jobjectArray stackTraceArray = Java_test_com_jni_HelloJNI_getStackTraceNative(env, obj, throwable);
    if (stackTraceArray == NULL) {
        printf("无法获取堆栈跟踪\n");
        return;
    }

    // 获取数组长度
    jsize length = (*env)->GetArrayLength(env, stackTraceArray);
    // 获取StackTraceElement类
    jclass stackTraceElementClass = (*env)->FindClass(env, "java/lang/StackTraceElement");
    // 获取getClassName、getMethodName、getFileName、getLineNumber方法的ID
    jmethodID getClassNameMethod = (*env)->GetMethodID(env, stackTraceElementClass, "getClassName", "()Ljava/lang/String;");
    jmethodID getMethodNameMethod = (*env)->GetMethodID(env, stackTraceElementClass, "getMethodName", "()Ljava/lang/String;");
    jmethodID getFileNameMethod = (*env)->GetMethodID(env, stackTraceElementClass, "getFileName", "()Ljava/lang/String;");
    jmethodID getLineNumberMethod = (*env)->GetMethodID(env, stackTraceElementClass, "getLineNumber", "()I");

    // 遍历数组打印每一行堆栈信息
    printf("堆栈跟踪信息:\n");
    for (jsize i = 0; i < length; i++) {
        jobject element = (*env)->GetObjectArrayElement(env, stackTraceArray, i);
        // 调用方法获取各个字段值
        jstring className = (jstring)(*env)->CallObjectMethod(env, element, getClassNameMethod);
        jstring methodName = (jstring)(*env)->CallObjectMethod(env, element, getMethodNameMethod);
        jstring fileName = (jstring)(*env)->CallObjectMethod(env, element, getFileNameMethod);
        jint lineNumber = (*env)->CallIntMethod(env, element, getLineNumberMethod);

        // 转换为C字符串并打印
        const char *classNameStr = (*env)->GetStringUTFChars(env, className, NULL);
        const char *methodNameStr = (*env)->GetStringUTFChars(env, methodName, NULL);
        const char *fileNameStr = (*env)->GetStringUTFChars(env, fileName, NULL);

        printf("\tat %s.%s(%s:%d)\n", classNameStr, methodNameStr, fileNameStr, lineNumber);

        // 释放资源
        (*env)->ReleaseStringUTFChars(env, className, classNameStr);
        (*env)->ReleaseStringUTFChars(env, methodName, methodNameStr);
        (*env)->ReleaseStringUTFChars(env, fileName, fileNameStr);
        (*env)->DeleteLocalRef(env, element);
    }

    // 释放局部引用
    (*env)->DeleteLocalRef(env, stackTraceElementClass);
    (*env)->DeleteLocalRef(env, stackTraceArray);
}

关键步骤说明

  • 加载本地库:Java代码里通过static代码块调用System.loadLibrary("hello"),加载编译好的libhello.so(Linux/macOS)或hello.dll(Windows)。
  • Native方法声明:用native关键字标记需要JNI实现的方法,方法签名要和C/C++端的函数名严格对应(格式为Java_包名_类名_方法名,包名的.换成_)。
  • JNI调用Java方法:在C代码中通过JNIEnv指针的函数,先获取对应的类和方法ID,再调用CallObjectMethod等方法执行Java端的方法,获取堆栈跟踪数组后遍历处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:16:18