浅谈 JNIEnv 和 JavaVM

浅谈 JNIEnv 和 JavaVM一 概念 1 JavaVmJavaVM 是虚拟机在 JNI 层的代表 一个进程只有一个 JavaVM 所有的线程共用一个 JavaVM 2 JNIEnvJNIEnv 表示 nbsp Java 调用 native 语言的环境 是一个封装了几乎全部 JNI 方法的指针 JNIEnv 只在创建它的线程生效 不能跨线程传递 不同线程的 JNIEnv 彼此独立 native 环境中创建的线程

一、概念

1. JavaVm

JavaVM 是虚拟机在 JNI 层的代表,一个进程只有一个 JavaVM,所有的线程共用一个 JavaVM。

2. JNIEnv

JNIEnv 表示 Java 调用 native 语言的环境,是一个封装了几乎全部 JNI 方法的指针。

JNIEnv 只在创建它的线程生效,不能跨线程传递,不同线程的 JNIEnv 彼此独立。

native 环境中创建的线程,如果需要访问 JNI,必须要调用 AttachCurrentThread 关联,并使用 DetachCurrentThread 解除链接。

 

二、两种代码风格(C/C++)

JavaVM 和 JNIEnv 在 C 语言环境下和 C++ 环境下调用是有区别的,主要表现在:

C风格:(*env)->NewStringUTF(env, “Hellow World!”);

C++风格:env->NewStringUTF(“Hellow World!”);

建议使用 C++ 风格,这也是大部分代码使用的形式。

注意:C++ 风格其实只是封装了 C 风格,使得调用更加简介方便。

 

三、定义

1. JavaVM 和 JNIEnv 在

中的定义如下:

#if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif

这里分了 C 和 C++。如果是 C++ 环境下,则只是对 _JNIEnv 和 _JavaVM 的一个重命名;如果是 C 环境下,则是指向 JNINativeInterface 结构体和 JNIInvokeInterface 结构体的指针。

 

2. 继续看 JNINativeInterface 结构体和 JNIInvokeInterface 结构体的定义

struct JNINativeInterface { ... jint (*GetVersion)(JNIEnv *); jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); jclass (*FindClass)(JNIEnv*, const char*); ... }; struct JNIInvokeInterface { void* reserved0; void* reserved1; void* reserved2; jint (*DestroyJavaVM)(JavaVM*); jint (*AttachCurrentThread)(JavaVM*, JNIEnv, void*); jint (*DetachCurrentThread)(JavaVM*); jint (*GetEnv)(JavaVM*, void, jint); jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv, void*); };

出于篇幅考虑,这里省略了一下 JNINativeInterface 结构体,它里面还包含了大量的方法。

所以我们可以知道,C 风格下的 JavaVM 和 JNIEnv 就是指向的这两个结构体的指针,通过这两个指针我们可以调用到这两个结构体里的各个方法。那么 C++ 风格下的 _JNIEnv 和 _JavaVM 又是怎么定义的呢?

 

3. _JNIEnv 和 _JavaVM

struct _JNIEnv { /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; #if defined(__cplusplus) jint GetVersion() { return functions->GetVersion(this); } jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen) { return functions->DefineClass(this, name, loader, buf, bufLen); } jclass FindClass(const char* name) { return functions->FindClass(this, name); } ... #endif /*__cplusplus*/ }; struct _JavaVM { const struct JNIInvokeInterface* functions; #if defined(__cplusplus) jint DestroyJavaVM() { return functions->DestroyJavaVM(this); } jint AttachCurrentThread(JNIEnv p_env, void* thr_args) { return functions->AttachCurrentThread(this, p_env, thr_args); } jint DetachCurrentThread() { return functions->DetachCurrentThread(this); } jint GetEnv(void env, jint version) { return functions->GetEnv(this, env, version); } jint AttachCurrentThreadAsDaemon(JNIEnv p_env, void* thr_args) { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); } #endif /*__cplusplus*/ };

这里还是省略了很多 _JNIEnv 里的方法。

通过上面代码我们可以看到,_JNIEnv 和 _JavaVM 其实只是对 JNINativeInterface 和 JNIInvokeInterface 结构体的一层封装,实际调用和操作的还是 JNINativeInterface 和 JNIInvokeInterface 里的方法。

 

四、总结

以上,我们可以简单的了解和认识到 JavaVM 和 JNIEnv 在 JNI 开发中扮演的角色,我们 JNI 的绝大多数操作都是通过这两者来调用到 JNINativeInterface 和 JNIInvokeInterface 结构体里的相关方法

其中 JavaVM 是一个全局变量,一个进程只有一个 JavaVM 对象。

而 JNIEnv 是一个线程拥有一个,不同线程的 JNIEnv 彼此独立。

最后由于 JavaVM 和 JNIEnv 在 C/C++ 环境下的实现不同,所以产生了两种代码风格,即:

C风格:(*env)->NewStringUTF(env, “Hellow World!”);

C++风格:env->NewStringUTF(“Hellow World!”);

其它:

​NDK 学习系列:Android NDK 从入门到精通(汇总篇)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/204488.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午8:11
下一篇 2026年3月19日 下午8:11


相关推荐

  • YOLO系列之yolo v2

    YOLO系列之yolo v2v2 算法在 v1 的基础上可以说是飞跃性提升 吸取诸子百家之优势 同时 v2 的论文也是 yolo 系列论文里干货最多的文章 美中不足的是 v2 论文里并没有像 v1 那种网络结构图 如图 1 所示 论文标题 YOLO9000 Better Faster Stronger 论文地址 yolo v2 的一大特点是可以 tradeoff 翻译成中文就是 折中 v2 可以在速度和准确率上进行

    2026年3月19日
    2
  • mysql乐观锁实现

    mysql乐观锁实现各锁的概念 悲观锁 假定会发生并发冲突 屏蔽一切可能违反数据完整性的操作悲观锁 从字面理解就是很悲观 每次去拿数据的时候都认为别人会修改 所以在每次拿的时候对数据上锁 这样就保证了数据的准确性 比如 mysql 中的表锁 行锁 表锁 当你对一张表进行修改时 会锁死整张表 其他的请求需要在修改完成释放锁才能继续 在高并发的情景下不适用 行锁 当你对一张表的某一行数据修改时 会锁死这一行数

    2026年3月26日
    2
  • JSP 九大内置对象及四个作用域详解

    JSP 九大内置对象及四个作用域详解JSP 中一共预先定义了 9 个内置对象 内置对象 又叫做隐含对象 不需要预先声明就可以在脚本代码和表达式中随意使用 request response session application out pagecontext config page exceptionreq 请求对象 类型 javax servlet ServletReque

    2026年3月20日
    2
  • OpenAI推理模型负责人Jerry Tworek离职,将探索新研究方向

    OpenAI推理模型负责人Jerry Tworek离职,将探索新研究方向

    2026年3月15日
    2
  • 电商后台管理系统前端开发总结[通俗易懂]

    电商后台管理系统前端开发总结[通俗易懂]前端和服务器存在跨域问题使用token维持状态,否则使用cookie或者session记录状态

    2022年5月18日
    53
  • pycharm 2021年4月激活码_通用破解码

    pycharm 2021年4月激活码_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    96

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号