JNI编程指南.pdf





《JNI编程指南.pdf》由会员分享,可在线阅读,更多相关《JNI编程指南.pdf(50页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、序这四种情况下你会用到本书:1、在 Java程序中复用以前写过的C/C+代码。2、自己实现一个java 虚拟机3、学习不同语言如何进行协作,尤其是如何实现垃圾回收和多线程。4、把一个虚拟机实现整合到用C/C+写的程序中。本书是写给开发者的。JNI 在 1997 年第一次发布,本书总结了SUN 工程师和大量开发者两年来积累的经验。本书介绍了JNI 的设计思想,对这种思想的理解是使用JNI 的各种特性的基础。本书有一部分是JAVA2 平台上面的JNI 特征的规范说明。JNI 程序员可以把这部分用作一个手册。JVM 开发者在实现虚拟机的时候必须遵守这些规范。JNI 的部分设计思想来源于Netscap
2、e的 Java Runtime Interface(JRI)。第一章简介JNI 是 JAVA 平台的一个重要特征,使用它我们可以重用以前用C/C+写的大量代码。本书既是一个编程指南也是一个JNI 手册。本书共包括三部分:1、第二章通过一个简单的例子介绍了JNI。它的对象是对JNI 不熟悉的初学者。2、310 章对 JNI 的特征进行了系统的介绍。我们会举大量的例子来说明JNI 的各个特征,这些特征都是JNI 中重要且常用的。3、1113 章是关于JNI 的技术规范。可以把这两章当作一个手册。本书尽量去满足各类读者的需要。指南面向初学者,手册面向有经验的人和自己实现JNI规范的人。大部分读者可能
3、是用JNI 来写程序的开发者。本书会假设你有JAVA,C/C+基础。本章的剩余部分介绍了JNI 的背景,扮演的角色和JNI 的演化。1.1 JAVA 平台和系统环境(Host Environment)系统环境代指本地操作系统环境,它有自己的本地库和CPU 指令集。本地程序(Native Applications)使用C/C+这样的本地语言来编写,被编译成只能在本地系统环境下运行的二进制代码,并和本地库链接在一起。本地程序和本地库一般地会依赖于一个特定的本地系统环境。比如,一个系统下编译出来的C 程序不能在另一个系统中运行。1.2 JNI 扮演的角色JNI 的强大特性使我们在使用JAVA 平台的
4、同时,还可以重用原来的本地代码。作为虚拟机实现的一部分,JNI 允许 JAVA 和本地代码间的双向交互。图 1.1 JNI 的角色JNI 可以这样与本地程序进行交互:1、你可以使用JNI 来实现“本地方法”(native methods),并在 JAVA 程序中调用它们。2、JNI 支持一个“调用接口”(invocation interface),它允许你把一个JVM 嵌入到本地程序中。本地程序可以链接一个实现了JVM 的本地库,然后使用“调用接口”执行 JAVA 语言编写的软件模块。例如,一个用C 语言写的浏览器可以在一个嵌入式JVM 上面执行从网上下载下来的applets 1.3 JNI
5、的副作用请记住,一旦使用JNI,JAVA 程序就丧失了JAVA 平台的两个优点:1、程序不再跨平台。要想跨平台,必须在不同的系统环境下重新编译本地语言部分。2、程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。一个通用规则是,你应该让本地方法集中在少数几个类当中。这样就降低了JAVA 和 C 之间的耦合性。1.4 什么场合下应该使用JNI 当你开始着手准备一个使用JNI 的项目时,请确认是否还有替代方案。像上一节所提到的,应用程序使用JNI 会带来一些副作用。下面给出几个方案,可以避免使用JNI 的时候,达到与本地代码进行交互的效果:1、JAVA 程序和本地程序使用TCP/IP 或
6、者 IPC 进行交互。2、当用 JAVA 程序连接本地数据库时,使用JDBC 提供的 API。3、JAVA 程序可以使用分布式对象技术,如JAVA IDL API。这些方案的共同点是,JAVA 和 C 处于不同的线程,或者不同的机器上。这样,当本地程序崩溃时,不会影响到JAVA 程序。下面这些场合中,同一进程内JNI 的使用无法避免:1、程序当中用到了JAVA API 不提供的特殊系统环境才会有的特征。而跨进程操作又不现实。2、你可能想访问一些己有的本地库,但又不想付出跨进程调用时的代价,如效率,内存,数据传递方面。3、JAVA 程序当中的一部分代码对效率要求非常高,如算法计算,图形渲染等。总
7、之,只有当你必须在同一进程中调用本地代码时,再使用JNI。1.5 JNI 的演化JDK1.0 包含了一个本地方法接口,它允许JAVA 程序调用C/C+写的程序。许多第三方的程序和 JAVA 类库,如:java.lang,java.io, 等都依赖于本地方法来访问底层系统环境的特征。不幸的是,JDK1.0 中的本地方法有两个主要问题:1、本地方法像访问C 中的结构(structures)一样访问对象中的字段。尽管如此,JVM 规范并没有定义对象怎么样在内存中实现。如果一个给定的JVM实现在布局对象时,和本地方法假设的不一样,那你就不得不重新编写本地方法库。2、因为本地方法可以保持对JVM 中对象
8、的直接指针,所以,JDK1.0 中的本地方法采用了一种保守的GC 策略。JNI 的诞生就是为了解决这两个问题,它可以被所有平台下的JVM 支持:1、每一个 VM 实现方案可以支持大量的本地代码。2、开发工具作者不必处理不同的本地方法接口。3、最重要的是,本地代码可以运行在不同的JVM 上面。JDK1.1 中第一次支持JNI,但是,JDK1.1 仍在使用老风格的本地代码来实现JAVA 的 API。这种情况在JDK1.2 下被彻底改变成符合标准的写法。1.6 例子程序本书包含了大量的代码示例,还教我们如何使用javah 来构建 JNI 程序。第二章开始。本章通过一个简单的例子来示例如何使用JNI。
9、我们写一个JAVA 程序,并用它调用一个C函数来打印“Hello World!”。21 概述图 2.1 演示了如何使用JAVA 程序调用C 函数来打印“Hello World!”。这个过程包含下面几步:1、创建一个类(HelloWorld.java)声明本地方法。2、使用 javac 编译源文件HollowWorld.java,产生 HelloWorld.class。使用 javah jni 来生成C 头文件(HelloWorld.h),这个头文件里面包含了本地方法的函数原型。3、用 C 代码写函数原型的实现。4、把 C 函数实现编译成一个本地库,创建Hello-World.dll或者 lib
10、Hello-World.so。5、使用 java 命令运行HelloWorld程序,类文件HelloWorld.class和本地库(HelloWorld.dll或者 libHelloWorld.so)在运行时被加载。图 2.1 编写并运行“HelloWorld”程序本章剩余部分会详细解释这几步。第三章基本类型、字符串、数组开发者使用JNI 时最常问到的是JAVA 和 C/C+之间如何传递数据,以及数据类型之间如何互相映射。本章我们从整数等基本类型和数组、字符串等普通的对象类型开始讲述。至于如何传递任意对象,我们将在下一章中进行讲述。3.1 一个简单的本地方法JAVA 端源代码如下:class
11、Prompt /native method that prints a prompt and reads a line private native String getLine(String prompt);public static void main(String args)Prompt p=new Prompt();String input=p.getLine(Type a line:);System.out.println(User typed:+input);static System.loadLibrary(Prompt);3.1.1 本地方法的C 函数原型Prompt.getL
12、ine 方法可以用下面这个C 函数来实现:JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv*env,jobject this,jstring prompt);其中,JNIEXPORT 和 JNICALL这两个宏(被定义在jni.h)确保这个函数在本地库外可见,并且 C 编译器会进行正确的调用转换。C 函数的名字构成有些讲究,在11.3 中会有一个详细的解释。3.1.2 本地方法参数第一个参数JNIEnv 接口指针,指向一个个函数表,函数表中的每一个入口指向一个JNI 函数。本地方法经常通过这些函数来访问JVM 中的数据结构。图 3.1 演
13、示了 JNIEnv 这个指针:图 3.1 JNIEnv 接口指针第二个参数根据本地方法是一个静态方法还是实例方法而有所不同。本地方法是一个静态方法时,第二个参数代表本地方法所在的类;本地方法是一个实例方法时,第二个参数代表本地方法所在的对象。我们的例子当中,Java_Prompt_getLine 是一个实例方法,因此jobject参数指向方法所在的对象。3.1.3 类型映射本地方法声明中的参数类型在本地语言中都有对应的类型。JNI 定义了一个C/C+类型的集合,集合中每一个类型对应于JAVA 中的每一个类型。JAVA 中有两种类型:基本数据类型(int,float,char 等)和引用类型(类
14、,对象,数组等)。JNI 对基本类型和引用类型的处理是不同的。基本类型的映射是一对一的。例如JAVA 中的int 类型直接对应C/C+中的 jint(定义在jni.h 中的一个有符号32 位整数)。12.1.1 包含了JNI 中所有基本类型的定义。JNI 把 JAVA 中的对象当作一个C 指针传递到本地方法中,这个指针指向JVM 中的内部数据结构,而内部数据结构在内存中的存储方式是不可见的。本地代码必须通过在JNIEnv 中选择适当的JNI 函数来操作JVM 中的对象。例如,对于java.lang.String 对应的 JNI 类型是jstring,但本地代码只能通过GetStringUTFC
15、hars 这样的 JNI 函数来访问字符串的内容。所有的 JNI 引用都是jobject 类型,对了使用方便和类型安全,JNI 定义了一个引用类型集合,集合当中的所有类型都是jobject 的子类型。这些子类型和JAVA 中常用的引用类型相对应。例如,jstring 表示字符串,jobjectArray 表示对象数组。3.2 访问字符串Java_Prompt_getLine 接收一个jstring 类型的参数prompt,jstring 类型指向JVM 内部的一个字符串,和常规的C 字符串类型char*不同。你不能把jstring 当作一个普通的C 字符串。3.2.1 转换为本地字符串本地代码
16、中,必须使用合适的JNI 函数把jstring 转化为C/C+字符串。JNI 支持字符串在Unicode 和 UTF-8 两种编码之间转换。Unicode 字符串代表了16-bit 的字符集合。UTF-8 字符串使用一种向上兼容7-bit ASCII 字符串的编码协议。UTF-8 字符串很像NULL 结尾的 C字符串,在包含非ASCII 字符的时候依然如此。所有的7-bitASCII 字符的值都在1127 之间,这些值在UTF-8 编码中保持原样。一个字节如果最高位被设置了,意味着这是一个多字节字符(16-bitUnicode 值)。函数Java_Prompt_getLine通过调用JNI 函
17、数GetStringUTFChars来读取字符串的内容。GetStringUTFChars 可以把一个jstring 指针(指向 JVM 内部的 Unicode 字符序列)转化成一个 UTF-8 格式的 C 字符串。如何你确信原始字符串数据只包含7-bit ASCII 字符,你可以把转化后的字符串传递给常规的C库函数使用,如 printf。我们会在 8.2中讨论如何处理非ASCII字符串。JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv*env,jobject obj,jstring prompt)char buf128;const jb
18、yte*str;str=(*env)-GetStringUTFChars(env,prompt,NULL);if(str=NULL)return NULL;/*OutOfMemoryError already thrown*/printf(%s,str);(*env)-ReleaseStringUTFChars(env,prompt,str);/*We assume here that the user does not type more than *127 characters*/scanf(%s,buf);return 不要忘记检查 GetStringUTFChars。因为 JVM需要为
19、新诞生的 UTF-8字符串分配内存,这个操作有可能因为内存太少而失败。失败时,GetStringUTFChars 会返回 NULL,并抛出一个 OutOfMemoryError 异常(对异常的处理在第6 章)。这些JNI 抛出的异常与 JAVA中的异常是不同的。一个由JNI 抛出的未决的异常不会改变程序执行流,因此,我们需要一个显示的return语句来跳过 C函数中的剩余 语 句。Java_Prompt_getLine函 数 返 回 后,异 常 会 在Prompt.main(Prompt.getLine这个发生异常的函数的调用者)中抛出,3.2.2 释放本地字符串资源从 GetStringUT
20、FChars 中获取的 UTF-8字符串在本地代码中使用完毕后,要使用ReleaseStringUTFChars告诉 JVM这个 UTF-8 字符串不会被使用了,因为这个UTF-8字符串占用的内存会被回收。3.2.3 构造新的字符串你 可 以 通 过JNI函 数NewStringUTF 在 本 地 方 法 中 创 建 一 个 新 的java.lang.String字符串对象。这个新创建的字符串对象拥有一个与给定的UTF-8编码的 C类型字符串内容相同的Unicode 编码字符串。如果一个 VM不能为构造 java.lang.String分配足够的内存,NewStringUTF会抛出一个 Out
21、OfMemoryError 异常,并返回一个 NULL。在这个例子中,我们不必检查它的返回值,因为本地方法会立即返回。如果NewStringUTF 失败,OutOfMemoryError 这个异常会被在Prompt.main(本地方法的调用者)中抛出。如果 NeweStringUTF 成功,它会返回一个JNI 引用,这个引用指向新创建的java.lang.String对象。这 个对象 被 Prompt.getLine返回 然 后被赋 值给Prompt.main 中的本地 input。3.2.4 其它 JNI 字符串处理函数JNI 支持许多操作字符串的函数,这里做个大致介绍。GetStringC
22、hars和 ReleaseStringChars获取以 Unicode 格式编码的字符串。当操作系统支持 Unicode 编码的字符串时,这些方法很有用。UTF-8 字符串以 0 结尾,而Unicode 字符串不是。如果jstring指向一个Unicode 编码的字符串,为了得到这个字符串的长度,可以调用 GetStringLength。如果一个 jstring指向一个 UTF-8编码的字符串,为了得到这个字符串的字节长度,可以调用标准C 函数strlen。或者直接对jstring调用JNI 函数GetStringUTFLength,而不用管 jstring指向的字符串的编码格式。GetStr
23、ingChars和 GetStringUTFChars 函数中的第三个参数需要更进一步的解释:const jchar*GetStringChars(JNIEnv*env,jstring str,jboolean*isCopy);当从 JNI 函数 GetStringChars中返回得到字符串B 时,如果 B 是原始字符串java.lang.String的拷贝,则 isCopy 被赋值为 JNI_TRUE。如果 B和原始字符串指向的是 JVM中的同一份数据,则isCopy 被赋值为 JNI_FALSE。当 isCopy 值为JNI_FALSE时,本地代码决不能修改字符串的内容,否则JVM中的原始
24、字符串也会被修改,这会打破JAVA语言中字符串不可变的规则。通常,因为你不必关心JVM是否会返回原始字符串的拷贝,你只需要为isCopy传递 NULL作为参数。JVM是否会通过拷贝原始Unicode 字符串来生成 UTF-8字符串是不可以预测的,程序员最好假设它会进行拷贝,而这个操作是花费时间和内存的。一个典型的JVM会在 heap上为对象分配内存。一旦一个 JAVA字符串对象的指针被传递给本地代码,GC就不会再碰这个字符串。换言之,这种情况下,JVM必须 pin 这个对象。可是,大量地 pin 一个对象是会产生内存碎片的,因为,虚拟机会随意性地来选择是复制还是直接传递指针。当你不再使用一个从
25、GetStringChars得到的字符串时,不管JVM内部是采用复制还是直接传递指针的方式,都不要忘记调用ReleaseStringChars。根据方法GetStringChars是复制还是直接返回指针,ReleaseStringChars会释放复制对象时所占的内存,或者unpin 这个对象。3.2.5 JDK1.2中关于字符串的新JNI 函数为了提高JVM返回字符串直接指针的可能性,JDK1.2 中引入了一对新函数,Get/ReleaseStringCritical。表面上,它们和 Get/ReleaseStringChars函数差不多,但实际上这两个函数在使用有很大的限制。使用这两个函数时
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- JNI 编程 指南

限制150内