文档章节

Java本地接口JNI详解

恰同学少年
 恰同学少年
发布于 2015/08/07 23:39
字数 2036
阅读 623
收藏 36

对于java程序员来说,java语言的好处和优点,我想不用我说了,大家自然会说出很多一套套的。但虽然我们作为java程序员,但我们不得不承认java语言也有一些它本身的缺点。比如在性能、和底层打交道方面都有它的缺点。所以java就提供了一些本地接口,他主要的作用就是提供一个标准的方式让java程序通过虚拟机与原生代码进行交互,这也就是我们平常常说的java本地接口(JNI——java native Interface。它使得在 Java 虚拟机(VM) 内部运行的Java 代码能够与用其它编程语言(如 CC++ 和汇编语言)编写的应用程序和库进行互操作。JNI 最重要的好处是它没有对底层Java 虚拟机的实现施加任何限制。因此,Java虚拟机厂商可以在不影响虚拟机其它部分的情况下添加对JNI 的支持。程序员只需编写一种版本的本地应用程序或库,就能够与所有支持JNI Java 虚拟机协同工作。我们来看一下为什么要与原生代码进行交互:


一:提高应用程序性能。我们知道java对于c/c++、汇编语言来说,显得比较“高级”。其实这里的高级就是简化了程序员的工作。很多底层的东西都让java虚拟机做了。但毕竟相对于直接访问底层来讲,java多了一步虚拟机的过程,所以在性能上比着这些原生语言稍微有点慢。


二:实现一些与底层相关的功能。Java平台提供的标准类库,还有强大的API,虽然能完成大部分功能。但有些和底层硬件打交道的功能在java API提供的类库中还是无法完成。


三:与已有的使用原生代码编写的程序进行集成在于操作系统上由c或者c++等原生语言编写的软件进行集成的时候,可以用JNI


JNI 接口函数和指针

平台相关代码是通过调用 JNI 函数来访问Java 虚拟机功能的。JNI 函数可通过接口指针来获得。接口指针是指针的指针,它指向一个指针数组,而指针数组中的每个元素又指向一个接口函数。每个接口函数都处在数组的某个预定偏移量中。下图说明了接口指针的组织结构。

JNI 接口的组织类似于C++ 虚拟函数表或COM 接口。使用接口表而不使用硬性编入的函数表的好处是使JNI 名字空间与平台相关代码分开。虚拟机可以很容易地提供多个版本的JNI 函数表。例如,虚拟机可支持以下两个JNI 函数表:

1)一个表对非法参数进行全面检查,适用于调试程序;

2)另一个表只进行 JNI 规范所要求的最小程度的检查,因此效率较高。

JNI 接口指针只在当前线程中有效。因此,本地方法不能将接口指针从一个线程传递到另一个线程中。实现JNI 的虚拟机可将本地线程的数据分配和储存在JNI 接口指针所指向的区域中。


本地方法将JNI 接口指针当作参数来接受。虚拟机在从相同的 Java 线程中对本地方法进行多次调用时,保证传递给该本地方法的接口指针是相同的。但是,一个本地方法可被不同的Java 线程所调用,因此可以接受不同的JNI 接口指针。

1)编写Java类代码

其中,需要JNI实现的方法应当用native关键字声明,在该类中,用System.loadLibrary()方法加载需要的动态链接库,关键代码如下:

//Compute.java
public class Compute{
    public native double sqrt(double params);
    static{
        //调用动态链接库
        System.loadLibrary("compute");
    }
}

    
2)编译成字节代码

在这个过程中,由于采用了native关键字声明,Java编译器会忽视没有代码体的JNI方法部分。
   
3)生成相关JNI方法的头文件

这个过程的实现一般是通过利用jlavah-jni  * class生成的(-jni可以省略),也可以手工生成该文件;但是由于 Java 虚拟机是根据一定的命名规范完成对JNI方法的调用,所以手工编写头文件需要特别小心。
上述文件产生的头文件部分代码如下:

//Compute.h
extern"C"{
JNIEXPORT jdoubleJNICALL Java_Compute_comp(JNI-Env *, jobject, jdoubleArray);

JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。


JNI函数的参数也由三部分组成:首先是JNIEnv *,是一个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同一一非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其Java类的引用;其余的参数对应通常Java方法的参数,参数类型需要根据一定规则进行映射。


4)编写相应方法的实现代码

在编码过程中,需要注意变量的长度问题,例如Java的整型变量长度为32位,而C语言为16位,所以要仔细核对变量类型映射表,防止在传值过程中出现问题。


 5)JNI实现代码编译成动态链接库

编译过程是利用CC++编译器实现的,在windows平台上,编译和连接的结果是动态链接库DLL文件。当要使用生成的动态链接库时,调用者类中需要显式调用该链接库dll文件
经过上述处理,基本上完成了一个包含本地化方法的Java类的开发。


附录:将Jav类型映射到本地类型 

为了使用方便,特提供以下定义。

#define JNI_FALSE  0

#define JNI_TRUE   1

jsize 整数类型用于描述主要指数和大小:

typedef jint jsize;


故障排除

当使用 JNI Java 程序访问本机代码时,您会遇到许多问题。您会遇到的三个最常见的错误是:

1)无法找到动态链接。它所产生的错误消息是:java.lang.UnsatisfiedLinkError。这通常指无法找到共享库,或者无法找到共享库内特定的本机方法。

2)无法找到共享库文件。当用 System.loadLibrary(String libname) 方法(参数是文件名)装入库文件时,请确保文件名拼写正确以及没有指定扩展名。还有,确保库文件的位置在类路径中,从而确保JVM 可以访问该库文件。

3)无法找到具有指定说明的方法。确保您的C/C++ 函数实现拥有与头文件中的函数说明相同的说明。


结束语

从 Java 调用C++ 本机代码(虽然不简单)是Java 平台中一种良好集成的功能。虽然JNI 支持C++,但C++ 接口更清晰一些并且通常比接口更可取。正如您已经看到的,调用C++ 本机代码需要赋予函数特殊的名称,并创建共享库文件。当利用现有代码库时,更改代码通常是不可取的。要避免这一点,在C++ 中,通常创建代理代码或代理类,它们有专门的JNI 所需的命名函数。然后,这些函数可以调用底层库函数,这些库函数的说明和实现保持不变。

本文转载自:http://blog.csdn.net/csh624366188/article/details/8063144

恰同学少年
粉丝 14
博文 70
码字总数 23613
作品 0
深圳
高级程序员
私信 提问
JNI和NDK的区别

NDK(Native Development Kit)“原生”也就是二进制 android常用的开发方式是java封装的库,而这些库的底层实现是由C/C++实现,如媒体,图形库等 java调用这样实现就需要用JNI(Java Native...

长平狐
2013/01/06
107
0
jni详解(摘自《jni详解》)

本书涵盖了 Java Native Interface(JNI)的内容,将探讨以下问题: • 在一个 Java 项目中集成一个 C/C++库 • 在一个用 C/C++开发的项目中,嵌入 JavaVM • 实现 Java VM • 语言互操作性问题...

wangjian19
2014/02/22
7K
0
Java程序员从笨鸟到菜鸟之(九十六)深入java虚拟机(五)——java本地接口JNI详解

对于java程序员来说,java语言的好处和优点,我想不用我说了,大家自然会说出很多一套套的。但虽然我们作为java程序员,但我们不得不承认java语言也有一些它本身的缺点。比如在性能、和底层打...

长平狐
2012/11/12
154
0
JNI和NDK的区别

NDK(Native Development Kit)“原生”也就是二进制 android常用的开发方式是java封装的库,而这些库的底层实现是由C/C++实现,如媒体,图形库等 java调用这样实现就需要用JNI(Java Native...

鉴客
2012/03/09
215
0
JNI和NDK的区别

NDK(Native Development Kit)“原生”也就是二进制 android常用的开发方式是java封装的库,而这些库的底层实现是由C/C++实现,如媒体,图形库等 java调用这样实现就需要用JNI(Java Native...

晨曦之光
2012/03/01
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS 7 搭建源码包搭建LNMP环境

1,安装epel源 yum install -y epel-release 2, 安装需要的软件 yum install -y gcc automake autoconf libtool make gcc-c++ glibc libxslt-devel \libjpeg libjpeg-devel libpng libpng......

hansonwong
40分钟前
5
0
基于 K8s 做应用发布的工具那么多, 阿里为啥选择灰姑娘般的 Tekton ?

作者 | 邓洪超,阿里云容器平台工程师, Kubernetes Operator 第二人,云原生应用标准交付与管理领域知名技术专家 导读:近年来,越来越多专门给 Kubernetes 做应用发布的工具开始缤纷呈现,...

阿里巴巴云原生
42分钟前
2
0
ZStack的普惠云计算到底是“随大流”还是真功夫?(转自社区)

作为一名深耕技术圈的“老男人”,也算是经历过云计算领域的成长。近些年云计算技术不断成熟,百行百业开始云上之路,各大云计算厂商开始逐渐意识到云计算是一项“普惠”技术。 我一直很看好...

ZStack社区版
43分钟前
3
0
一个备份MySQL数据库的简单Shell脚本

Shell脚本是我们写不同类型命令的一种脚本,这些命令在这一个文件中就可以执行。我们也可以逐一敲入命令手动执行。如果我们要使用shell脚本就必须在一开始把这些命令写到一个文本文件中,以后...

旺仔大战肥五花
47分钟前
2
0
TiDB Binlog 源码阅读系列文章(四)Pump server 介绍

作者: satoru 在 上篇文章 中,我们介绍了 TiDB 如何通过 Pump client 将 binlog 发往 Pump,本文将继续介绍 Pump server 的实现,对应的源码主要集中在 TiDB Binlog 仓库的 pump/server.go...

TiDB
52分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部