Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么JNICAPI对JNIEnv使用指向指针的指针而不是直接指针?_Java_C_Java Native Interface_Programming Languages_Jnienv - Fatal编程技术网

Java 为什么JNICAPI对JNIEnv使用指向指针的指针而不是直接指针?

Java 为什么JNICAPI对JNIEnv使用指向指针的指针而不是直接指针?,java,c,java-native-interface,programming-languages,jnienv,Java,C,Java Native Interface,Programming Languages,Jnienv,免责声明:我已经实现了我自己的面向对象编程语言,因此我熟悉并教授了指针和C语言。内存管理101不是这个问题的核心。这也不是关于看起来类似的JNI C++ API。多谢各位 当您查看JNI C API for Java时,在JNI.h中,JNIEnv是指向结构JNINativeInterface的指针的typedef。鉴于所有JNI C API都采用JNIEnv*,这意味着它实际上是一个JNINativeInterface**,这意味着指向结构的指针 为什么JNI要使用额外的间接层 在C中模拟类似

免责声明:我已经实现了我自己的面向对象编程语言,因此我熟悉并教授了指针和C语言。内存管理101不是这个问题的核心。这也不是关于看起来类似的JNI C++ API。多谢各位

当您查看JNI C API for Java时,在JNI.h中,JNIEnv是指向结构JNINativeInterface的指针的typedef。鉴于所有JNI C API都采用JNIEnv*,这意味着它实际上是一个JNINativeInterface**,这意味着指向结构的指针

为什么JNI要使用额外的间接层

在C中模拟类似对象的构造在JNITINAVEINTERFACE*中可以正常工作。你可以打个电话

env->NewGlobalRef(env, my_object);
那么,为什么要强迫我们这样做呢

(*env)->NewGlobalRef(env, my_object);
(*env)->NewGlobalRef((*env), my_object);
??额外的间接级别作为第一个参数传递到函数中,因此我猜它们可以更新该指针。是这样吗

更正: 我最初记错了,并将*env而不是env作为第一个参数传递,因此排除了被调用方编辑指针本身的可能性。我已经更正了帖子。感谢您指出这一点。

JNIEnv实际上不是指向指针的指针,而是指向包含其他专用线程特定信息的数据结构的指针。JNITINAVEINTERFACE*只是结构中的第一个字段,其余字段不是公共字段。这使得VM实现JNI函数表具有更大的灵活性

此处提供一些链接,以方便可能遇到此问题的人:

-这里说明:

JNI接口指针JNIEnv*仅在当前线程中有效。不能将接口指针从一个线程传递到另一个线程,也不能缓存接口指针并在多个线程中使用它。Java虚拟机将在来自同一线程的本机方法的连续调用中向您传递相同的接口指针,但不同的线程向本机方法传递不同的接口指针

JNIEnv实际上不是指向指针的指针,而是指向包含其他私有线程特定信息的数据结构的指针。JNITINAVEINTERFACE*只是结构中的第一个字段,其余字段不是公共字段。这使得VM实现JNI函数表具有更大的灵活性

此处提供一些链接,以方便可能遇到此问题的人:

-这里说明:

JNI接口指针JNIEnv*仅在当前线程中有效。不能将接口指针从一个线程传递到另一个线程,也不能缓存接口指针并在多个线程中使用它。Java虚拟机将在来自同一线程的本机方法的连续调用中向您传递相同的接口指针,但不同的线程向本机方法传递不同的接口指针

在jni.h中,有一条评论:

<>我们使用C++的内联函数,程序员可以在C++中编写Env- > FordCub java/LAN/String,而不是:*./Env:>FunCaleNeV,Java/Lang/String,在C.

例如,在结构JNIENVI中,如果C++,则有一种方法:

jclass FindClass(const char *name) {
    return functions->FindClass(this, name);
}
该类有一个指针:

const struct JNINativeInterface_ *functions;
[这是C唯一可见的东西]。它是指向虚拟函数表的指针

因此,AFAICT,C的正确定义是:

env->functions->FindClass(env,name)
这是成员函数执行调用的方式,并且与上面引用的注释相匹配

那么,你确定:

工作

恰好*env->FindClassenv,name起作用,因为函数是第一个元素[对于C来说是唯一的元素]

因此,对于我来说,我要创建一个宏,它执行以下操作:

#define DEREF(_env) ((_env)->functions)
DEREF(env)->FindClass(env,name)
在jni.h中,有一条评论:

<>我们使用C++的内联函数,程序员可以在C++中编写Env- > FordCub java/LAN/String,而不是:*./Env:>FunCaleNeV,Java/Lang/String,在C.

例如,在结构JNIENVI中,如果C++,则有一种方法:

jclass FindClass(const char *name) {
    return functions->FindClass(this, name);
}
该类有一个指针:

const struct JNINativeInterface_ *functions;
[这是C唯一可见的东西]。它是指向虚拟函数表的指针

因此,AFAICT,C的正确定义是:

env->functions->FindClass(env,name)
这是成员函数执行调用的方式,并且与上面引用的注释相匹配

那么,你确定:

工作

恰好*env->FindClassenv,name起作用,因为函数是第一个元素[对于C来说是唯一的元素]

因此,对于我来说,我要创建一个宏,它执行以下操作:

#define DEREF(_env) ((_env)->functions)
DEREF(env)->FindClass(env,name)
TL;DR:多种因素导致了JNI的设计,其中包括二进制兼容性、多线程支持和所需的接口特性,以及由此产生的C调用范式

JNI调用习惯用法 那么,为什么要强迫我们这样做呢

(*env)->NewGlobalRef(env, my_object);
(*env)->NewGlobalRef((*env), my_object);
??额外级别的间接寻址不会作为 第一个参数,因此他们无法更新该指针

这不是JNI调用的正确形式,事实上,正如分布式JNI头文件所明确指出的,正确的形式是

JNIEnv *env = /* ... */;

(*env)->NewGlobalRef(env, my_object);
请注意

在C API中,JNIEnv本身就是一个指针 pe as是将->运算符应用于*env所必需的,尤其是 传递给JNI函数的是env本身,而不是*env。也就是说,与问题的断言相反,额外的间接性被传递给JNI函数。 JNI设计注意事项 虽然花了一些时间去思考,并阅读了一些信息性的部分,但是,我改变了我的想法,设计的目的是为了清洁的C++接口,而牺牲了一个稍微不那么干净的C接口。JNI C接口需要设计为使用它所使用的调用习惯用法的唯一技术原因是,JNI函数可以将一个环境换成另一个环境,但是没有理由认为任何JNI函数都会这样做,也没有理由认为任何JNI函数都会这样做。有关此设计选择的更多评论,请稍后参阅

提供尽可能多的官方报道。它讨论了JNI当前第二次主要迭代的历史背景和设计目标。特别是,Sun的立场是,设计良好的界面具有以下优点:

每个VM供应商都可以支持更多的本机代码。 工具构建器不必维护不同种类的本机方法接口。 应用程序程序员将能够编写一个版本的本机代码,该版本将在不同的虚拟机上运行。 在与各相关方协商后,他们达成了以下高层次的设计要求:

二进制兼容性——主要目标是本地方法库在Java虚拟机上的所有Java虚拟机实现之间的二进制兼容性 给定平台。程序员应该只维护其应用程序的一个版本 给定平台的本机方法库。 效率-为了支持时间关键型代码,本机方法接口必须施加很少的开销。所有已知的技术,以确保 VM独立性和二进制兼容性带来了一定的影响 在头顶上。我们必须设法在效率和效率之间达成妥协 和VM独立性。 功能性-接口必须公开足够的Java VM内部构件,以允许本机方法完成有用的任务。 这些文档表达了对COM作为实现这些目标的接口技术的高度赞赏,事实上,微软已经为其Java1VM创建了一个COM接口。但当然,COM也有一些问题,不仅是在与Java的技术细节方面,还有在感兴趣的平台(包括Sun自己的Solaris)上不可用的小问题。因此,我认为这可能是对所提出问题的真正答案:

尽管Java对象不作为COM公开给本机代码 对象,JNI接口本身与COM是二进制兼容的。JNI 使用与COM相同的跳转表结构和调用约定 做这意味着,一旦跨平台支持COM 如果可用,JNI可以成为JavaVM的COM接口

原文的重点

最终JNI设计 本规范还提供了一个关键部分:

本机代码通过调用JNI函数访问JavaVM特性。JNI 函数可通过接口指针使用。接口 指针是指向指针的指针。这个指针指向一个数组 指针,每个指针指向一个接口函数。每一个 接口函数位于阵列内部的预定义偏移处

这正是我们实际上看到的,并且规范继续说明它如何类似于C++虚拟函数表和COM接口。它还阐明了使用函数表具有以下优点:

将JNI命名空间与本机命名空间隔离 允许同一个VM在不同的上下文中提供可选的函数表 此外,它还解释了提供指向函数表的双指针有助于向不同线程呈现不同的表:

JNI接口指针仅在当前线程中有效。A. 因此,本机方法不能从一个 穿线到另一个。实现JNI的VM可以分配和存储 JNI接口指针指向的区域中的线程本地数据

本机方法接收JNI接口指针作为参数。这个 VM保证向本机方法传递相同的接口指针 当它从同一个Java对本机方法进行多次调用时 线但是,可以从不同的Java调用本机方法 线程,因此可能接收不同的JNI接口指针

JNI接口指针是前面提到的双指针,其类型在C JNI中表示为JNIEnv*

结论 C调用范式直接遵循数据和接口设计。必须取消对JNI接口指针的引用才能获取函数表指针和接口指针itsel f、 传递给每个函数的不是函数表指针

同样的事情也发生在C++ API中,但是它是通过包装一个类中的函数表指针和伪装JNI接口指针作为指向该类实例的指针来伪装的。这也提供了一个提供包装函数的机会,包装函数掩盖了JNI接口指针被传递给JNI函数的事实。我把这当作C++语言的特点,用这种语言提供简单而自然的接口,而不是C++设计的JNI的第一个例子。

< P> TL;DR:多种因素导致了JNI的设计,其中包括二进制兼容性、多线程支持和所需的接口特性,以及由此产生的C调用范式

JNI调用习惯用法 那么,为什么要强迫我们这样做呢

(*env)->NewGlobalRef(env, my_object);
(*env)->NewGlobalRef((*env), my_object);
??额外级别的间接寻址不会作为 第一个参数,因此他们无法更新该指针

这不是JNI调用的正确形式,事实上,正如分布式JNI头文件所明确指出的,正确的形式是

JNIEnv *env = /* ... */;

(*env)->NewGlobalRef(env, my_object);
请注意

在C API中,JNIEnv本身是一种指针类型,这是将->运算符应用于*env所必需的,尤其是 传递给JNI函数的是env本身,而不是*env。也就是说,与问题的断言相反,额外的间接性被传递给JNI函数。 JNI设计注意事项 虽然花了一些时间去思考,并阅读了一些信息性的部分,但是,我改变了我的想法,设计的目的是为了清洁的C++接口,而牺牲了一个稍微不那么干净的C接口。JNI C接口需要设计为使用它所使用的调用习惯用法的唯一技术原因是,JNI函数可以将一个环境换成另一个环境,但是没有理由认为任何JNI函数都会这样做,也没有理由认为任何JNI函数都会这样做。有关此设计选择的更多评论,请稍后参阅

提供尽可能多的官方报道。它讨论了JNI当前第二次主要迭代的历史背景和设计目标。特别是,Sun的立场是,设计良好的界面具有以下优点:

每个VM供应商都可以支持更多的本机代码。 工具构建器不必维护不同种类的本机方法接口。 应用程序程序员将能够编写一个版本的本机代码,该版本将在不同的虚拟机上运行。 在与各相关方协商后,他们达成了以下高层次的设计要求:

二进制兼容性——主要目标是本地方法库在Java虚拟机上的所有Java虚拟机实现之间的二进制兼容性 给定平台。程序员应该只维护其应用程序的一个版本 给定平台的本机方法库。 效率-为了支持时间关键型代码,本机方法接口必须施加很少的开销。所有已知的技术,以确保 VM独立性和二进制兼容性带来了一定的影响 在头顶上。我们必须设法在效率和效率之间达成妥协 和VM独立性。 功能性-接口必须公开足够的Java VM内部构件,以允许本机方法完成有用的任务。 这些文档表达了对COM作为实现这些目标的接口技术的高度赞赏,事实上,微软已经为其Java1VM创建了一个COM接口。但当然,COM也有一些问题,不仅是在与Java的技术细节方面,还有在感兴趣的平台(包括Sun自己的Solaris)上不可用的小问题。因此,我认为这可能是对所提出问题的真正答案:

尽管Java对象不作为COM公开给本机代码 对象,JNI接口本身与COM是二进制兼容的。JNI 使用与COM相同的跳转表结构和调用约定 做这意味着,一旦跨平台支持COM 如果可用,JNI可以成为JavaVM的COM接口

原文的重点

最终JNI设计 本规范还提供了一个关键部分:

本机代码通过调用JNI函数访问JavaVM特性。JNI 函数可通过接口指针使用。接口 指针是指向指针的指针。这个指针指向一个数组 指针,每个指针指向一个接口函数。每一个 接口函数位于阵列内部的预定义偏移处

这正是我们实际上看到的,并且规范继续说明它如何类似于C++虚拟函数表和COM接口。它还阐明了使用函数表具有以下优点:

将JNI命名空间与本机命名空间隔离 允许相同的虚拟机 在不同的上下文中提供可选的函数表 此外,它还解释了提供指向函数表的双指针有助于向不同线程呈现不同的表:

JNI接口指针仅在当前线程中有效。A. 因此,本机方法不能从一个 穿线到另一个。实现JNI的VM可以分配和存储 JNI接口指针指向的区域中的线程本地数据

本机方法接收JNI接口指针作为参数。这个 VM保证向本机方法传递相同的接口指针 当它从同一个Java对本机方法进行多次调用时 线但是,可以从不同的Java调用本机方法 线程,因此可能接收不同的JNI接口指针

JNI接口指针是前面提到的双指针,其类型在C JNI中表示为JNIEnv*

结论 C调用范式直接遵循数据和接口设计。必须取消对JNI接口指针的引用才能获得函数表指针,并且接口指针本身(而不是函数表指针)被传递给每个函数


同样的事情也发生在C++ API中,但是它是通过包装一个类中的函数表指针和伪装JNI接口指针作为指向该类实例的指针来伪装的。这也提供了一个提供包装函数的机会,包装函数掩盖了JNI接口指针被传递给JNI函数的事实。我把这当作C++函数的好用法,用这种语言提供一个简单而自然的接口,而不是作为C++第一个设计JNI的证据。垃圾收集器在其自己的线程上运行。。。如果GC可以移动这个指针,那么在没有锁的情况下取消引用它是不安全的。此外,考虑到目前大多数操作系统都有虚拟内存,让GC在内存中重新定位对象似乎不太常见。你有更多信息的链接吗?这有帮助吗?关于线程-JNI接口指针JNIEnv*仅在当前线程中有效。不能将接口指针从一个线程传递到另一个线程,也不能缓存接口指针并在多个线程中使用它。Java虚拟机将在来自同一线程的本机方法的连续调用中向您传递相同的接口指针,但不同的线程向本机方法传递不同的接口指针。谢谢,Oracle链接有帮助。该图显示,JNIEnv实际上不是指向指针的指针,而是包含其他私有线程特定信息的数据结构。JNITINAVEINTERFACE*只是结构中的第一个字段,其余字段不是公共字段。它还解释了它不是关于垃圾收集,而是关于能够提供不同版本的函数:```使用接口表的优点是JNI名称空间变得独立……VM可以轻松地提供多个版本的JNI函数表```你想用这些信息更新你的答案吗?你给我指出了正确的答案,所以我很乐意接受:尽管这看起来很奇怪。垃圾收集器在其自己的线程上运行。。。如果GC可以移动这个指针,那么在没有锁的情况下取消引用它是不安全的。此外,考虑到目前大多数操作系统都有虚拟内存,让GC在内存中重新定位对象似乎不太常见。你有更多信息的链接吗?这有帮助吗?关于线程-JNI接口指针JNIEnv*仅在当前线程中有效。不能将接口指针从一个线程传递到另一个线程,也不能缓存接口指针并在多个线程中使用它。Java虚拟机将在来自同一线程的本机方法的连续调用中向您传递相同的接口指针,但不同的线程向本机方法传递不同的接口指针。谢谢,Oracle链接有帮助。该图显示,JNIEnv实际上不是指向指针的指针,而是包含其他私有线程特定信息的数据结构。JNITINAVEINTERFACE*只是结构中的第一个字段,其余字段不是公共字段。它还解释了它不是关于垃圾收集,而是关于能够提供不同版本的函数:```使用接口表的优点是JNI名称空间变得独立……VM可以轻松地提供多个版本的JNI函数表```你想用这些信息更新你的答案吗?你给我指出了正确的答案,所以我很乐意接受它:D.J.Ni。H,有一个注释:我们使用C++的内联函数,这样程序员就可以在C++中编写Env->FordCalpave/Lang/String,而不是在C.中用*Env->FordCaleNeV,java/LAN/String作为顶部斜体文本,这个问题不是关于C++ API的。但这解释了为什么它是一个双点


呃。他们为C++编写了它,使它不太适合C。参见Dmitri Pisklov的回答和我的评论。它实际上不是一个双指针!,公共标题只是让它看起来像一个。C++描述似乎只是描述了一个围绕C API的语法糖,以便更好地使用。@ UILISTION,当JNI.H由C编译器编译时,每个JNI函数的第一个参数是const结构JNIATIVE接口F**-双指针,因为该参数被声明为JNIENV*,当在C++中编译头时,JNIENV是C++结构的JNNE.java接口的一个TyPulf。JNI.H有一个注释:我们使用C++的内联函数,这样程序员可以在C++中编写Env-> FordCaseJava/Lang/String,而不是在顶部C.的斜体文本中的*EnV--FoeCopyNeV,Java/Lang/String。这个问题不是关于C++ API的。不,但是它解释了为什么它是双指针。他们为C++编写了它,使它不太适合C。参见Dmitri Pisklov的回答和我的评论。它实际上不是一个双指针!,公共标题只是让它看起来像一个。C++描述似乎只是描述了一个围绕C API的语法糖,以便更好地使用。@ UILISTION,当JNI.H由C编译器编译时,每个JNI函数的第一个参数是const结构JNIATIVE接口F**-双指针,因为该参数被声明为JNIENV*,当用C编译头文件时,JNIEnv是const struct JNINativeInterface_173的类型定义。这也是我的主要评估。我的主要证据是C++接口如何利用Struts JNIENVI中的成员函数中的这个函数和函数的相等性。我们在C接口中有一个双指针,因为我们在C++接口中也有一个双指针,而且我猜想附加的间接在C++方面是有用的-也许只是为了便于封装C接口。但是,注意JNI.H玩一些游戏来定义类型。我认为这是您错误的结论的原因,即C的正确deref应该是env->functions->FindClassenv,name。事实并非如此。在C中,JNIENV是一个结构JNIATIVE接口FA*,就像OP所说的,不是一个结构JNENVVY注释,它是C++中的下划线。在C中,*env没有成员命名函数。但是OP也是错误的:正确的C调用应该是*env->FindClassenv,name而不取消对第一个参数的引用。@JohnBollinger您是对的。我错过了JNIEnv的typedef。但是,进一步看,对于c++:env->FindClassname,但对于c,JNIEnv env=。。。;,它是env->FindClass&env,name[而不是*env->FindClassenv,name],因为FindClass[在jniationveinterface_u2;中]接受JNIEnv*,它是一个双指针,但是,env是一个单指针,所以我们需要为arg使用&env,但是*env->无论什么都是错误的,除非在FindClass的实际实现中有更多的技巧!?你的想法?其他方式:JNIEnv*env=*环境->FindClassenv,名称;每个C接口函数的第一个参数的数据类型是JNIEnv*注意:没有下划线,也称为JNITINAVEINTERFACE**。这就是我们正在讨论的环境类型,所以1是的,它是一个双指针;通过这个参数接收的值当然已经是传递给其他JNI函数的正确类型*env->FindClassenv,name是正确的形式,这也证明了这一点。这主要也是我的评估。我的主要证据是C++接口如何利用Struts JNIENVI中的成员函数中的这个函数和函数的相等性。我们在C接口中有一个双指针,因为我们在C++接口中也有一个双指针,而且我猜想附加的间接在C++方面是有用的-也许只是为了便于封装C接口。但是,注意JNI.H玩一些游戏来定义类型。我认为这是您错误的结论的原因,即C的正确deref应该是env->functions->FindClassenv,name。事实并非如此。在C中,JNIENV是一个结构JNIATIVE接口FA*,就像OP所说的,不是一个结构JNENVVY注释,它是C++中的下划线。在C中,*env没有成员命名函数。但是OP也是错误的:正确的C调用应该是*env->FindClassenv,name而不取消对第一个参数的引用。@JohnBollinger您是对的。我错过了JNIEnv的typedef。但是,进一步看,对于c++:env->FindClassname,但对于c,JNIEnv env=。。。;,它是env->FindClass&env,name[而不是*env->FindClassenv,name],因为FindClass[在jniationveinterface_u2;中]接受JNIEnv*,它是一个双指针,但是,env是一个单指针,所以我们需要为arg使用&env,但是*env->无论什么都是错误的,除非在FindClass的实际实现中有更多的技巧!?你的想法?其他方式:JNIEnv*env=*环境->FindClassenv,名称;每个C接口函数的第一个参数的数据类型是JNIEnv*no 没有下划线,也称为JNINativeInterface。这就是我们正在讨论的环境类型,所以1是的,它是一个双指针;通过这个参数接收的值当然已经是传递给其他JNI函数的正确类型*env->FindClassenv,name是正确的形式,这也证明了这一点。