Jni解决了哪些问题?
Jni原理
JNI是Java Native Interface(Java本地接口)的缩写。JNI作为java和操作系统间的一个直接接口,可以通过JNI使得java直接调用操作系统的资源。目前JNI只能通过c/C++实现,因为jni只是对操作系统资源调用的一个桥接过程。所以理论上在windows下只要是dll文件均可以被调用。java代码编译之后是运行在一个jvm里,所以java的任何操作对操作系统而言都是隔着一层虚拟机外壳,这点也正式java的优点,帮助java实现了“Write Once, Run Everywhere”的可移植性。但是使用了jni之后必须要明白这个“Write Once, Run Everywhere”要被打破,必须要实现不同的操作系统的各种jni版本。
JNI的开发调用过程可以用下图来完整表示:
如果这个图表示的不够清楚,可以看下面这个图:
内存管理
通过jni创建的对象,都是使用jvm的heap空间。比如jstring、jarray等继承于jobject的类内存的分配实际上都是由jvm管理的,并不是使用c的本地内存。如以下代码片段创建了一个String对象,这个对象可以被以jstring的方式传递给jni框架的调用者,最终在jvm的heap里创建了一个内容为"hello world!"的String对象
char *buf = "hello world!";
(*env)->NewStringUTF(env, buf);
在jni代码里,任何jni基本类型的指针或者对象的创建都是通过直接分配内存的,如果创建的对象是被返回给jni调用者的,那么可以不用管理该对象的内存。如果是临时使用,则必须在使用完成之后释放内存
以下情况需要释放内存:
const jbyte *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
...
(*env)->ReleaseStringUTFChars(env, prompt, str);
jstring jstr = env->NewStringUTF((*p).sess_id);
...
env->DeleteLocalRef( jstr);
jobject jobj = env->NewObject(clazz,midInit);
return jobj;
所以一般对这类分配空间的操作都是成对出现:
GetStringUTFChars
ReleaseStringUTFChars
GetStringCritical
ReleaseStringCritical
GetStringRegion
SetStringRegion
Get<Type>ArrayElements
Release<Type>ArrayElements
GetPrimitiveArrayCritical
ReleasePrimitiveArrayCritical
应用场景
总结了一下,jni一般有以下一些应用场景
1.高性能 ,在一些情况下因为处理运算量非常大,为了获取高性能,直接使用java是不能胜任的,如:一些图形的处理
2.调用一些硬件的驱动或者一些软件的驱动,比如调用一些外部系统接口的驱动,如:读卡器的驱动,OCI驱动
3.需要使用大内存,远远超过jvm所能分配的内存,如:进程内Cache
4.调用C或者操作系统提供的服务,如:java调用搜索服务,其中搜索是由C/C++实现的,不过这个一般可以设计成更加通用的方式,比如soa的方式
所有这些场景的前提是牺牲了java代码的可移植性,不同的os,甚至版本都需要写不同版本的native实现
性能
java既然运行在jvm之上,那么jvm又是怎么和操作系统交互的呢?jvm是由c语言写成,对java的接口进行了优化,性能非常的好。
这点也让我以为jni接口调用会性能会非常好,实际情况测试表明jni接口的的调用性能是比较差的
以下是在JDK 1.6.0_07-b06版本windows下的测试结果:
1. 准备了两个空方法,分别是java版本和jni版本的helloNothing,方法里面不包含任何逻辑,测试分为10组,每组循环1亿次
HelloNothing h = new HelloNothing();
int times = 100000000; //亿次
// test native method
for (int n = 0; n < 10; n++) {
long t1 = System.currentTimeMillis();
for (int i = 0; i < times; i++) {
h.helloNothing1();
}
long t2 = System.currentTimeMillis();
System.out.println("round " + n + ": " + (t2 - t1) + "ms");
}
// test java method
for (int n = 0; n < 10; n++) {
long t1 = System.currentTimeMillis();
for (int i = 0; i < times; i++) {
h.helloNothing();
}
long t2 = System.currentTimeMillis();
System.out.println("round " + n + ": " + (t2 - t1) + "ms");
}
java直接调用 helloNothing():
private void helloNothing1(){
// do nothing
}
round 0: 125ms
round 1: 140ms
round 2: 157ms
round 3: 140ms
round 4: 125ms
round 5: 125ms
round 6: 140ms
round 7: 141ms
round 8: 141ms
round 9: 141ms
平均时间:137 ms 每秒执行:7.2亿次
java调用jni的 helloNothing():
private native void helloNothing();
JNIEXPORT void JNICALL Java_com_taobao_pcache_jni_HelloNothing_helloNothing
(JNIEnv *env, jobject obj)
{
// do nothing
}
测试结果:
round 0: 2453ms
round 1: 2156ms
round 2: 2187ms
round 3: 2156ms
round 4: 2157ms
round 5: 2187ms
round 6: 2156ms
round 7: 2156ms
round 8: 2141ms
round 9: 2156ms
平均时间:2190 ms 每秒执行:0.46亿次
java普通空方法和native空方法的调用效率相差在15倍左右,据说在jdk1.6之前的版本性能更差
2. 增加测试方法的复杂度,在方法里创建并返回一个String对象,测试循环1千万次
java被测试的方法:
private String helloString1(){
return "hello world!";
}
round 0: 328ms
round 1: 344ms
round 2: 328ms
round 3: 328ms
round 4: 344ms
round 5: 328ms
round 6: 328ms
round 7: 328ms
round 8: 344ms
round 9: 343ms
平均时间:334 ms 每秒执行:0.3亿次
native被测试的方法:
JNIEXPORT jstring JNICALL Java_com_taobao_pcache_jni_HelloNothing_helloString
(JNIEnv *env , jobject obj)
{
return (*env)->NewStringUTF(env, "hello world!");
}
测试结果:
round 0: 4234ms
round 1: 4484ms
round 2: 4297ms
round 3: 4140ms
round 4: 4094ms
round 5: 4296ms
round 6: 4532ms
round 7: 4391ms
round 8: 4390ms
round 9: 4406ms
平均时间:4326 ms 每秒执行:0.0231亿次
java普通字符串创建方法和native方法的调用效率相差在13倍左右,根据空方法的调用结果可以发现,通过jni new String对象和java直接new String对象消耗的时间上是差不多的,时间最大的消耗在于java调用jni方法的桥接上
。由于环境不一样,所以以上测试结果只是作为一个大致的参考
。
开发过程注意事项
1.windows下编译dll 命令行:
cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -MD -LD HelloNothing.c -FeHelloNothing.dll
linux下编译成so命令行:
gcc -I/opt/taobao/java/include -I/opt/taobao/java/include/linux -shared -fPIC HelloNothing.c -o libHelloNothing.so
2.dll/so加载问题:
类似下面的错误,表示dll或者so在环境变量路径里不能被找到
java.lang.UnsatisfiedLinkError: no HelloWorld in library path
at java.lang.Runtime.loadLibrary(Runtime.java)
at java.lang.System.loadLibrary(System.java)
at HelloWorld.main(HelloWorld.java)
解决方法:
1.将so或者dll拷贝到path变量指定的路径下
2.在执行的时候指定当前类路径为路径java.library.path的路径:java -Djava.library.path=. HelloWorld
3.如果是tomcat或者jboss则在启动脚本里配置java.library.path变量的路径
3.内存泄露
通过Jni创建的对象全部使用了堆内存,如以下代码片段创建了一个String对象,这个对象可以被以jstring的方式传递给jni框架的调用者,最终在jvm的heap里创建了一个内容为"hello world!"的String对象
char *buf = "hello world!";
(*env)->NewStringUTF(env, buf);
在jni代码里,任何jni对象的创建都是通过直接分配内存的,如果创建的对象是被返回给jni调用者的,那么可以不用管理该对象的内存。如果是临时使用,则必须在使用完成之后释放内存
java基本类型和jni基本类型对应表
Java Language Type Native Type Description
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
对象继承关系
分享到:
相关推荐
解决JNI中文乱码,
### 使用JNA替代JNI调用DLL,并解决内存溢出问题 #### 问题背景 在项目的开发过程中,常常遇到需要处理二进制流数据并对其进行解析处理的情况。这种情况下,如果上层应用平台采用的是Java开发,而底层算法或数据...
### jni需要注意的常见问题 #### 一、输入问题 ...通过以上步骤,可以有效地解决在使用 JNI 进行开发过程中遇到的一些常见问题。这些解决方案不仅可以帮助开发者避免编译错误,还能提高代码质量和可维护性。
1. 找不到jni.h文件:该问题可以通过将jdk的bin目录和include目录添加到编译环境中来解决。 2. 动态链接库加载失败:该问题可以通过检查动态链接库的路径和名称来解决。 Jni是一种powerful技术,allowing Java程序...
总之,这个压缩包提供了一种在Java环境中利用GMSSL国密算法的解决方案,通过JNI库和Java调用示例,可以帮助开发者轻松地在自己的Java应用中集成SM2、SM3和SM4算法,满足我国对信息安全的特殊需求。
JNI(Java Native Interface)是Java平台的一个重要组成部分,它提供了在Java代码中调用本地(非Java)代码的能力,...同时,`jni_md.h`的使用则涉及到跨平台开发,理解其作用可以帮助解决在不同操作系统上遇到的问题。
JAVAjni,使用JAVA操作INI增删改查文件,UTF-8版,解决中文乱码问题
JNI层内存泄漏检测工具是针对Android应用开发中的一个重要问题——JNI内存泄漏的解决方案。JNI,全称为Java Native Interface,允许Java代码与其他编程语言(如C++)交互,从而利用其性能优势。然而,由于Java和C/...
JNI是Java平台提供的一种机制,允许Java代码与用其他语言(如C或C++)编写的代码进行交互,从而实现性能优化或者调用特定平台的功能。 首先,我们需要创建一个Java类,声明native方法并定义调用本地接口的逻辑。...
第五本书籍“05134622333.pdf”可能是一本实战指南,提供了大量的代码示例和练习,帮助读者熟悉JNI的开发流程,提升解决实际问题的能力。 至于“tj3”这个文件名,由于没有明确的PDF扩展名,可能是另一个书籍的索引...
本教程将详细讲解如何使用JNI调用本地接口,特别是在处理中文字符传递时遇到的问题。 首先,我们需要理解JNI的基本结构。在Java端,我们会定义一个native方法,这个方法没有具体的实现,而是通过关键字`native`声明...
本解决方案主要针对如何通过JNI在Android设备上获取MAC地址。 MAC地址(Media Access Control Address)是网络设备的物理地址,每个网络接口控制器都有一个唯一的MAC地址,用于标识网络上的设备。在Android系统中,...
本篇文章将深入探讨这个问题,并提出一种另类的解决方案——利用JNI(Java Native Interface)进行加密和解密操作来规避乱码问题。 首先,我们需要理解为什么会出现中文乱码。这通常与字符编码有关,Java和JSP默认...
这使得Java可以充分利用现有库,提高性能,解决某些特定平台的问题,或者实现硬件级别的交互。 1. **JNI详解** JNI的详解通常会涵盖以下几个方面: - **JNI基础知识**:包括JNI函数的结构,本地方法注册,JNIEnv...
JNI(Java Native Interface)是一种技术,可以帮助解决Java访问底层硬件的局限和执行效率的提高。通过使用JNI,Java可以访问本地代码,从而实现与底层硬件的交互。 Delphi是一个功能强大的开发工具,它可以用来...
`coreUtil.sln`和`coreUtil.v11.suo`是Visual Studio的解决方案和用户设置文件,用于管理项目。 最后,Java代码通过`System.loadLibrary("hello")`加载我们编译好的本地库。运行Java程序时,`sayHello`方法会被调用...
因此,JNI作为标准化的解决方案,旨在解决这些问题,提供二进制兼容性、高效和广泛的功能支持。 **目标**是建立一个统一的本地接口标准,让开发者能够编写一次本地代码,在多个JVM实现上运行,减少维护成本,同时...
熟练掌握JNI,可以帮助开发者充分利用这两种语言的优势,解决特定的问题,提高应用程序的效率和功能。在Android开发中,结合NDK使用JNI,更是能够实现高性能、低级的系统交互,为游戏、多媒体应用等提供强大的支持。
JNI(Java Native Interface)是Java平台的标准组成部分,它允许Java代码和其他语言写的代码进行交互。在"jni-helloWorld"这个项目中,我们看到...而提供的错误处理和解决方案则有助于在实践中解决问题,提升开发效率。
这个"JNI测试用例代码及报告"包含了对JNI基础知识的实践应用,以及在初次尝试使用JNI时可能会遇到的问题和解决策略。下面将详细介绍JNI的基本概念、使用场景、测试用例的常见内容以及如何调试JNI相关的问题。 1. ...