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

如何通过JNI从已运行的JVM中获取JavaVM或JNIEnv实例

崩溃原因

你当前代码崩溃的核心问题是给JNI_GetCreatedJavaVMs传入了空指针作为输出参数。该函数的三个参数定义如下:

  1. 用于接收JVM实例指针的缓冲区地址
  2. 要获取的JVM实例最大数量
  3. 用于接收实际获取到的JVM数量的输出变量地址

你定义的jvm_ptr和count都是空指针,没有对应可写入的合法内存空间,函数执行时尝试向空地址写入数据,直接触发内存访问违例崩溃。

实现方法

DLL注入到Java进程后完全可以正常获取JNI环境,不需要Java侧主动发起调用,整体实现流程如下:

  1. 提前在栈上分配内存存储JVM指针和JVM数量,合法传入JNI_GetCreatedJavaVMs拿到已初始化的JavaVM实例
  2. 通过JavaVM指针调用AttachCurrentThread将当前注入的执行线程附加到JVM,拿到当前线程专属的JNIEnv指针,即可正常调用所有JNI能力
  3. 所有JNI操作完成后,调用DetachCurrentThread将线程从JVM分离,避免资源泄漏

核心实现代码参考:

use jni::sys::{
    JNI_GetCreatedJavaVMs, JNIEnv, JNI_VERSION_1_8, JavaVM, jint
};
use std::ptr::null_mut;

// 直接在栈上定义可变变量分配内存,不要传空指针
let mut jvm: *mut JavaVM = null_mut();
let mut jvm_count: jint = 0;

// 传入变量的可变地址作为输出缓冲区
let get_vm_status = unsafe {
    JNI_GetCreatedJavaVMs(
        &mut jvm as *mut *mut JavaVM,
        1,
        &mut jvm_count as *mut jint
    )
};
// JNI_OK对应值为0,返回非0、数量为0、指针为空都属于获取失败
if get_vm_status != 0 || jvm_count < 1 || jvm.is_null() {
    // 自行处理获取JVM失败的逻辑
    return;
}

// 附加当前线程到JVM,获取当前线程专属的JNIEnv
let mut env: *mut JNIEnv = null_mut();
let attach_status = unsafe {
    (**jvm).AttachCurrentThread.unwrap()(
        jvm,
        &mut env as *mut *mut JNIEnv as *mut *mut _,
        null_mut()
    )
};
if attach_status != 0 || env.is_null() {
    // 自行处理线程附加失败的逻辑
    return;
}

// 此处拿到的env可正常用于所有JNI调用,比如查找类、调用Java方法等

// JNI操作全部完成后,分离当前线程
unsafe {
    (**jvm).DetachCurrentThread.unwrap()(jvm);
}
避坑要点
  • 调用JNI_GetCreatedJavaVMs前先确认目标进程的jvm.dll已经加载完成、JVM初始化完毕,注入时机过早会导致拿不到JVM实例,甚至因为函数地址无效崩溃。
  • JNIEnv是线程绑定资源,禁止跨线程传递使用,其他线程需要调用JNI时必须单独执行Attach操作获取自己的env。
  • 注入的DLL必须和目标Java进程位数完全一致,32位、64位混用会直接触发内存错误。
  • Attach线程时指定的JNI版本要和目标Java版本匹配,日常使用传JNI_VERSION_1_8即可兼容Java 8及以上绝大多数版本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 21:39:16