Java Native Interface Specification Contents--Chapter 1: Indruction(翻译)

原文链接: https://docs.oracle.com/en/java/javase/16/docs/specs/jni/

第一章:介绍

       这一章介绍了Java Native Interface(Java本地接口)(JNI)。JNI是一个本地编程接口。它使得Java代码可以在Java虚拟机中,调用由其他编程语言所写的应用程序和库,如C,C++和汇编。
       JNI最大的好处是:它对底层JAVA虚拟机的实现没有任何限制。因此,Java虚拟机供应商可以添加对JNI的支持,而不会影响虚拟机的其他部分。程序员所写的同一版本本地应用程序,可以运行在所有能够支持JNI的Java虚拟机上[1]
       本章涵盖了以下主题:

  • Java Native Interface(Java本地接口)(JNI)总览
  • 历史背景
    • JDK 1.0 Native Method Interface(本地方法接口)
    • Java Runtime Interface(Java运行时接口)
    • Raw Native Interface(原生本地接口)和Java/COM Interface(接口)
  • 目的
  • Java Native Interface Approach(Java本地接口路径)
  • JNI编程

Java Native Interface(Java本地接口)(JNI)总览

       当你编写应用程序,全部都使用Java时,可能会存在一些情景和需求,是无法只使用Java就能完成的。程序员可以使用JNI来编写Java本地方法来解决这种问题。
       以下例子是关于何时需要使用Java本地方法的:

  • 应用程序需要使用标准Java类库无法支持的、平台相关的特性
  • 你已经有一个由其他编程语言写的库,但希望使用Java代码通过JNI调用它
  • 你想要通过低级语言(比如,汇编)实现一个时序要求严格的代码片段

       通过使用JNI编程,你可以使用本地方法:

  • 创建、查看、更新Java对象(包括数组和字符串)
  • 调用Java方法
  • catch和throw异常
  • 加载类和获取类信息
  • 执行运行时类型检查

你也可以通过Invocation API使用过JNI。它可以让任意本地应用嵌入Java虚拟机中。程序员通过Invocation API可以很容易让他们已有的Java应用嵌入,而不许与虚拟机代码进行链接。(翻译到目前,作者也不是很理解。但后续有专门章节介绍Invocation API的)

历史背景

不同供应商的虚拟机,提供了不同的本地方法接口。这些不同的接口,要求程序员编写、维护和发布不同版本的本地方法库,以支持特定的平台。简单地说,有以下本地方法接口:

  • JDK 1.0 本地方法接口
  • Netscape的Java Runtime Interface(Java运行时接口)
  • Microsoft的Raw Native Interface(原生方法接口) and Java/COM interface

JDK1.0 Native Method Interface(Java 1.0本地方法接口)

       JDK1.0推出时,就有本地方法接口。不幸地是,有两个主要原因导致JDK1.0的本地方法接口未被其他Java虚拟机采纳。
       首先,本地方法访问字段时,把Java对象作为C语言Structure(结构体)的Members(成员变量)。然而,Java语言规范并未定义对象如何在内存中存储。如果Java虚拟机在内存中存储对象的方式不同,程序员不得不重新编译本地方法库。
       其次,JDK1.0的本地方法接口依赖于传统的garbage collector(垃圾回收器)。比如,不受限制的使用unhand(非指针) macro(宏),以致于需要垃圾回收器保守地扫描本地栈。

Java Runtime Interface(Java运行时接口)

       Netscape早已提议Java运行时接口(JRI):以一个通用的接口提供给Java虚拟机使用。JRI的设计,考虑到了可移植性——它很少依赖Java虚拟机的具体实现细节。JRI解决了许多问题,包括:本地方法、调试、反射、内嵌(调用)等等。

Raw Native Interface(原生本地接口)和Java/COM[2] Interface(Java/COM接口)

       微软的Java虚拟机支持两种本地方法接口。在低层上,它提供了高效的Raw Native Interface(RNI)(原生本地接口)。虽然,RNI有一个主要的区别:与依赖于传统的垃圾回收机制不同的是,本地代码必须使用RNI函数显示调用垃圾回收器。但是,它提供了代码级的高层向后兼容[3]JDK的(native method interface)本地方法接口。
       在高层上,微软的(Java/COM)接口提供了一种无关(编程)语言的标准二进制接口给Java虚拟机。Java代码可以把COM对象当作Java对象使用。Java类也可以被当作COM类,供其他系统使用。

目的

       我们认为一个统一的、深思熟虑的标准接口,会给所有人提供以下好处:

  • 每个虚拟机提供商可以支持更多的本地代码。
  • 工具构建者无须维护不同类型的本地方法接口。
  • 开发应用的程序员只需要写一次本地代码,即可运行在不同的虚拟机上。
           实现一个本地方法接口的最好方法,就是让各个Java虚拟机参与相关讨论。因此,我们组织了一些列关于统一本地方法接口的Java许可证的研讨会。从讨论中可以得出的结论是,标准本地方法接口必须满足以下需求:
  • 二进制兼容性——主要目的是在一个特定平台上,所有Java虚拟机实现的本地方法库是二进制兼容的。
  • 效率——为了支持时间要求严格的代码,本地方法接口必须要减少额外的开销。众所周知,为了确保虚拟机无关性(即二进制兼容性)的技术,都会导致一定的开销。我们必须以某种方式,在效率和虚拟机无关性之间进行折中。
  • 功能——接口(这里指代本地方法接口)必须暴露足够的Java虚拟机内部构件,让本地方法可以完成有用的任务。

Java Native Interface Approach[4](Java本地接口方式)

       我们希望可以采用一种已经存在的方式作为标准接口。因为这样可以尽量减轻程序员的负担——不同的虚拟机有不同的接口。不幸地是,现存的方式没有一个可以完全满足我们的目的。
       Netscape的JRI(Java Runtime Interface)是最接近我们想象中的本地方法接口,因而我们把它作为我们设计的母版。熟悉JRI(Java Runtime Interface)的读者将会注意到JNI和JRI的相似之处,如:API命名规范、方法和字段ID的使用、本地和全局引用的使用等等。虽然,我们已经尽力了,但是JNI并不是对于JRI二进制兼容的。虽然,单个虚拟机可以同时支持JRI和JNI。
       微软的RNI是JDK1.0的改进产物。因为它解决了本地方法调用无法使用非传统垃圾收集器的问题。然而,RNI也并不可行。因为RNI的本地方法接口无法满足虚拟机无关性要求。RNI像JDK一样,本地方法存取Java对象为C语言结构体,导致了两个问题:

  • RNI暴露了Java对象的存内部存储布局给本地代码。
  • 使用C语言结构体直接存取Java对象使得它无法有效地处理"write barriers(写屏障)"。处理"write barriers(写屏障)"在高级垃圾收集机制算法中都是必要的。
           COM作为一种二进制标准,可以完全保证不同虚拟机之间的兼容性。调用一个COM方法,只需要一层非直接调用,仅会增加少量开销。此外,COM对象相比动态链接库,在解决版本问题上,有一个很大的进步。
           但是,使用COM作为Java本地方法调用接口因为一些原因,而被阻碍了:
  • 第一,Java/COM接口缺乏一些想要的功能,如获取private字段和抛出异常。
  • 第二,Java/COM接口自动提供了标准IUnnown和IDispatch COM接口给Java对象,因而本地方法可以获取public方法和字段。不幸地是,IDispatch接口并未支持Java方法重载,并且在方法命名上是忽略大小写区别的。并且,所有通过IDispatch接口暴露的方法,将被强制执行动态类型检查。这是因为IDispatch接口是被设计用于弱类型语言的(如Basic)。
  • 第三,COM是被设计用于软件组件(包括成熟的应用程序)一起工作的,而非处理个别低级语言方法调用的。我们觉得,把所有的的Java类和低级本地方法作为软件组件,是不合适的。
  • 第四,COM缺乏unix平台支持。
           虽然,Java对象并没有作为COM对象暴露给本地代码,JNI接口自身也和COM二进制兼容。JNI像COM一样使用,使用跳转表结构和调用协定。这意味着,只要COM支持跨平台,JNI就可以变成一个Java虚拟机的COM接口。
           JNI并不是Java虚拟机支持的唯一本地方法接口。一个标准的接口会使程序员受益。程序员只需要加载他们的本地代码库到不同的Java虚拟机。在某些情况下,程序员可能需要使用一些特定虚拟机接口的低级语言,以达到极高的执行效率。其他情况下,程序员应该会用高级接口来编译软件组件。事实上,当Java环境和组件的软件技术变得越来越成熟后,本地方法将会逐渐失去它们的重要性。

Programming to the JNI(JNI编程)

       JNI程序应该由本地方法程序员编写。使用JNI编程时,无需知道终端用户会将程序运行在哪个供应商提供的虚拟机上。运行本地库在Java虚拟机上,最好的方式就是遵循JNI标准。
       如果你实现了一个Java虚拟机,你也应该实现JNI。JNI已经经过了长时间地测试,并且不会增加任何程序运行开销和限制你的虚拟机实现,包括对象数据结构、垃圾回收机制等等。如果你在运行JNI时遇到了任何我们疏忽的问题,请把你的反馈发送给我们。

注解:
[1] 译者注,也就是一次编写,随处运行的意思。
[2] 译者注,COM指微软公司提出的COM组件技术。
[3] 译者注,向后兼容指兼容旧版本。文化差异导致,英美认为forward代表未来,backward代表过去。所以,向前兼容指向未来兼容,向后兼容指向过去兼容。
[4] 译者注,Approach翻译为方式,用于和方法加以区别。

猜你喜欢

转载自blog.csdn.net/afunx/article/details/118207030