JNI 即 Java Native Interface 是 native 编程接口,它允许在Java虚拟机(VM)内运行Java代码与其他编程语言(主要是C和C++)编写的应用程序和库进行交互操作。
JNI最重要的好处是它对底层Java VM的实现没有任何限制。因此,Java VM供应商可以添加对JNI的支持,而不会影响VM的其他部分。程序员可以编写一个native应用程序或库的版本,并期望它可以与支持JNI的所有Java VM一起使用。
本文主要将从以下几点讲述JNI相关的内容,用于我们了解JNI:
概述、背景、目标、实现方式。
一、JNI 概述
虽然您可以完全使用Java编写应用程序,但有些情况下Java本身并不能满足您的应用程序的需求。当应用程序无法完全用Java编写时,程序员使用JNI编写Java Native 方法来处理这些情况。
以下示例说明何时需要使用Java Native方法:
- 标准Java类库不支持应用程序所需的与平台相关的功能。
- 您已经有一个使用另一种语言编写的库,并希望通过JNI使其可以访问Java代码。
- 您希望在较低级别的语言(如C/C++)中实现一小部分时间关键代码。
通过JNI编程,您可以使用Native方法:
- 创建,检查和更新Java对象(包括数组和字符串)。
- 调用Java方法。
- 捕获并抛出异常。
- 加载类并获取类信息。
- 执行运行时类型检查。
二、JNI 产生的历史背景
来自不同供应商的VM提供了不同的Native方法接口。这些不同的接口迫使程序员在给定平台上生成,维护和分发多个版本的本机方法库。
我们简要介绍一些本机方法接口,例如:
- JDK 1.0本机方法接口
- Netscape的Java运行时接口
- Microsoft的原始本机接口和Java / COM接口
1、JDK 1.0本机方法接口
JDK 1.0 附带了Native方法接口。不幸的是,这个接口不适合其他Java VM采用有两个主要原因。
首先,本机代码访问Java对象中的字段作为C结构的成员。但是,Java语言规范没有定义对象在内存中的布局方式。如果Java VM在内存中以不同方式布局对象,则程序员必须重新编译本机方法库。
其次,JDK 1.0的本机方法接口依赖于保守的垃圾收集器。unhand
例如,不受限制地使用宏使得必须保守地扫描本机堆栈。
2、Java运行时接口
Netscape提出了Java运行时接口(JRI),它是Java虚拟机中提供的服务的通用接口。JRI的设计考虑了可移植性 - 它对底层Java VM中的实现细节做了很少的假设。JRI解决了广泛的问题,包括本机方法,调试,反射,嵌入(调用)等。
3、原始本机接口和Java / COM接口
Microsoft Java VM支持两种本机方法接口。在低级别,它提供了有效的原始本机接口(RNI)。RNI提供了与JDK本机方法接口的高度源级向后兼容性,尽管它有一个主要区别。本机代码必须使用RNI函数与垃圾收集器明确交互,而不是依赖于保守的垃圾收集。
在更高级别,Microsoft的Java / COM接口为Java VM提供了与语言无关的标准二进制接口。Java代码可以像使用Java对象一样使用COM对象。Java类也可以作为COM类公开给系统的其余部分。
三、JNI 产生的目标
我们相信,统一且经过深思熟虑的标准接口为每个人提供以下好处:
- 每个VM供应商都可以支持更多的natvie代码。
- 工具构建器不必维护不同类型的natvie方法接口。
- 应用程序编程人员将能够编写其natvie代码的一个版本,该版本将在不同的VM上运行。
实现标准本机方法接口的最佳方法是让所有各方都参与Java VM。因此,我们在Java许可证持有者之间组织了一系列关于统一本机方法接口设计的讨论。从讨论中可以清楚地看出,标准本机方法接口必须满足以下要求:
- 二进制兼容性 - 主要目标是在给定平台上的所有Java VM实现中对本机方法库进行二进制兼容。程序员应该只为给定平台维护其本机方法库的一个版本。
- 效率 - 为了支持时间关键代码,本机方法接口必须施加很少的开销。确保VM独立性(以及二进制兼容性)的所有已知技术都带有一定量的开销。我们必须以某种方式在效率和VM独立性之间达成妥协。
- 功能 - 接口必须公开足够的Java VM内部,以允许本机方法完成有用的任务。
四、实现JNI的方式讨论
我们希望采用现有方式之一作为标准,主要是为了给在不同VM中学习多个接口的程序员带来最小的负担。但是令人失望的是,目前没有现有的解决方案来实现我们这样的想法。
Netscape的JRI是最接近我们想要的的便携式Natvie方法接口,并被用作我们设计的起点。熟悉JRI的读者会注意到API命名约定,方法和字段ID的使用,本地和全局引用的使用等方面的相似之处。尽管我们尽最大努力,但JNI与JRI不是二进制兼容的,尽管VM可以同时支持JRI和JNI。
微软的RNI是对JDK 1.0的改进,因为它解决了使用非保守垃圾收集器的本机方法的问题。但是,RNI不适合作为独立于VM的本机方法接口。与JDK一样,RNI本机方法将Java对象作为C结构访问,但也导致两个问题:
- RNI将内部Java对象的布局暴露给本机代码。
- 直接访问Java对象作为C结构使得无法有效地合并“写入障碍”,这在高级垃圾收集算法中是必需的。
作为二进制标准,COM确保跨不同VM的完全二进制兼容性。调用COM方法只需要间接调用,这几乎不会产生任何开销。此外,COM对象在解决版本问题方面比动态链接库有了很大的改进。
但是,使用COM作为标准Java本机方法接口受到以下几个因素的阻碍:
- 首先,Java / COM接口缺少某些所需的功能,例如访问私有字段和引发一般异常。
- 其次,Java / COM接口自动为Java对象提供标准的IUnknown和IDispatch COM接口,以便本机代码可以访问公共方法和字段。遗憾的是,IDispatch接口不处理重载的Java方法,并且在匹配方法名称时不区分大小写。此外,通过IDispatch接口公开的所有Java方法都被包装以执行动态类型检查和强制。这是因为IDispatch接口在设计时考虑了弱类型语言(例如Basic)。
- 第三,COM不是处理单独的低级功能,而是旨在允许软件组件(包括完整的应用程序)协同工作。我们认为将所有Java类或低级本机方法视为软件组件是不合适的。
- 第四,由于缺乏对UNIX平台的支持,因此立即采用COM受到阻碍。
虽然Java对象不作为COM对象公开给本机代码,但JNI接口本身与COM二进制兼容。JNI使用与COM相同的跳转表结构和调用约定。这意味着,只要跨平台支持COM,JNI就可以成为Java VM的COM接口。
JNI不被认为是给定Java VM支持的唯一本机方法接口。标准接口使程序员受益,他们希望将本机代码库加载到不同的Java VM中。在某些情况下,程序员可能必须使用较低级别的VM特定接口来实现最高效率。在其他情况下,程序员可能使用更高级别的界面来构建软件组件。实际上,随着Java环境和组件软件技术的日趋成熟,本机方法将逐渐失去意义。
五、总结
首先本文是译文,原文地址为:https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/intro.html。
同时也是本人整理的JNI教程的第一篇,可能部分内容语法有点不通顺,但是看完了也能基本了解JNI是什么,产生的背景,以及JNI被实现的方式。
下面附一下学习JNI时推荐的参考资料:
- Java Native Interface官方技术手册
- Google JNI官方文档
- JNI教程与技术手册
- http://www.kancloud.cn/owenoranba/jni/120449
- JNI官方规范中文版
- Java本地接口(WIKI)
- Dalvik虚拟机JNI方法的注册过程分析
- JNI函数
- jni.h头详见详解二
- OpenJDK源码 /openjdk/hotspot/src/share/vm/prims/jni.h
- OpenJDK源码 /openjdk/hotspot/src/share/vm/prims/jni.c
- Android JNI编程提高篇之二
- JNI引用于垃圾回收
- http://blog.****.net/autumn20080101/article/details/8646431
- C++访问Java的String字符串对象