我在IBM上读到,
要访问Java对象的字段并调用它们的方法,本机代码必须调用FindClass()、GetFieldID()、GetMethodId()和GetStaticMethodID()。在GetFieldID()、GetMethodID()和GetStaticMethodID()的情况下,为给定类返回的ID在JVM进程的生存期内不会更改。但是获取字段或方法的调用可能需要在JVM中进行大量的工作,因为字段和方法可能是从超类继承的,这使得JVM沿着类层次结构向上查找它们。因为给定类的ID是相同的,所以应该查找一次,然后重用它们。类似地,查找类对象的开销可能很大,因此也应该缓存它们。
如何在JNI中缓存methodID
、fieldID
和class
对象?是否有内置的方法或必须遵循的特定程序?
5条答案
按热度按时间l7mqbcuq1#
没有内置的方法可供遵循,但是这里有一个标准的、简洁的、可重复的实现,展示了我如何实践IBM的建议。
我假设你从Java调用你的DLL,并且在你的应用程序生命周期中多次引用它。
示例本机Java类名为
org.stackoverflow.jni.NativeClazz
,它将实现2个内置JNI方法JNI_OnLoad()
和JNI_OnUnload()
。void JNI_OnLoad(JavaVM *vm, void *reserved)
:此方法将用于将Class ID注册为全局变量,并将Method ID和Field ID分配给静态变量。该方法在Java VM加载驱动程序时自动调用;它在驱动程序生命周期期间仅被调用一次。void JNI_OnUnload(JavaVM *vm, void *reserved)
:此方法将用于释放JNI_OnLoad()
注册的任何全局变量。VM将在应用程序关闭之前立即自动调用JNI_OnUnload()
。**理由:**据我所知,类ID必须注册为全局引用,以保持任何相关方法ID /字段ID的可行性。如果没有这样做,并且类从JVM中卸载,则在类重新加载时,方法ID/字段ID可能不同。如果类ID注册为全局引用,则关联的方法ID和字段ID不需要注册为全局引用。将类ID注册为全局引用可以防止卸载关联的Java类,从而稳定方法ID /字段ID值。
JNI_OnUnload()
中应该删除全局引用,包括类ID。方法ID和字段ID不由本机代码管理;它们由虚拟机管理,并且在卸载相关联的类之前是有效的。在虚拟机卸载定义类之前,无法显式删除字段ID和方法ID;它们可以在卸载后留给VM处理。
示例代码
以下C++代码部分中的注解解释了全局注册变量。
BeanObject
:*NativeClazz
:*NativeClazz
上使用javah
生成的C++头文件“org_stackoverflow_jni_NativeClazz.h
“:*kmbjn2e32#
下面是我如何实践IBM的建议。考虑一下这样的demo java类:
相应的jni头文件如下所示:
根据IBM的建议,我们需要缓存使用的类
SimpleClazz
和对象成员value
的字段id。在学习了这个很好的article之后,我将
SimpleClazz
缓存在函数JNI_OnLoad
中,该函数在加载本机库时调用(例如,通过System.loadLibrary)。在JNI_Onload
中,我们确实找到了class,并将这个jclass存储为一个全局字段。此外,在
getValue
的本机实现中,我们使用静态局部变量来缓存value
的字段id。这种设计是为了确保这个字段ID可以在更好的范围内,而不是在全局范围内。这种设计的缺点是每次调用这个函数时都需要与NULL进行比较。我从The Java Native Interface: Programmer's Guide and Specification这本书的4.4.1节学到了这个设计。最后,我们还需要编写函数
JNI_OnUnload
,当包含本机库的类加载器被垃圾收集时,将调用该函数。在这个函数中,我们释放了jclass的全局引用。我的cpp实现如下所示:
nmpmafwu3#
你可以有一些这样的实用程序结构:
这是从我的问题:https://stackoverflow.com/questions/10617714/how-to-extend-swt-com-support
一些很好的例子可以看看
os_structs.c
,它与eclipse SWT实现捆绑在一起。**注:**以上代码仅为示例,可适用于不同的操作系统。此外,它只是显示“如何访问Java字段”;对于方法,您可以遵循相同的方法。
3xiyfsfu4#
参考@JoshDM给出的答案
C接口与上面给出的C++接口略有不同。你必须这样写:
bfhwhh0e5#
对于简单的情况,我在C代码中使用静态字段,使用从Java类调用的initId方法初始化:
在C中: