`
wang吖
  • 浏览: 241091 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Jni原理 (转)

 
阅读更多

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原理 - winco - winco的博客
如果这个图表示的不够清楚,可以看下面这个图:
  Jni原理 - winco - winco的博客

 

 

内存管理

 

    通过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调用者的,那么可以不用管理该对象的内存。如果是临时使用,则必须在使用完成之后释放内存
以下情况需要释放内存:

Java代码
  1. const jbyte *str;   
  2. str = (*env)->GetStringUTFChars(env, prompt, NULL);   
  3. ...   
  4. (*env)->ReleaseStringUTFChars(env, prompt, str);   
  5.   
  6. jstring jstr = env->NewStringUTF((*p).sess_id);   
  7.    ...   
  8. env->DeleteLocalRef( jstr);   
  9.   
  10. jobject jobj = env->NewObject(clazz,midInit);   
  11. return jobj;  
 



所以一般对这类分配空间的操作都是成对出现:

 

Java代码
  1. GetStringUTFChars   
  2. ReleaseStringUTFChars   
  3. GetStringCritical   
  4. ReleaseStringCritical   
  5. GetStringRegion   
  6. SetStringRegion   
  7.   
  8. Get<Type>ArrayElements   
  9. Release<Type>ArrayElements   
  10. GetPrimitiveArrayCritical   
  11. 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亿次

Java代码
  1. HelloNothing h = new HelloNothing();   
  2.         int times = 100000000//亿次   
  3.         // test native method   
  4.         for (int n = 0; n < 10; n++) {   
  5.             long t1 = System.currentTimeMillis();   
  6.             for (int i = 0; i < times; i++) {   
  7.                 h.helloNothing1();   
  8.             }   
  9.             long t2 = System.currentTimeMillis();   
  10.             System.out.println("round " + n + ": " + (t2 - t1) + "ms");   
  11.         }   
  12.         // test java method   
  13.         for (int n = 0; n < 10; n++) {   
  14.             long t1 = System.currentTimeMillis();   
  15.             for (int i = 0; i < times; i++) {   
  16.                 h.helloNothing();   
  17.             }   
  18.             long t2 = System.currentTimeMillis();   
  19.             System.out.println("round " + n + ": " + (t2 - t1) + "ms");   
  20.         }  
 

java直接调用 helloNothing():

Java代码
  1. private void helloNothing1(){   
  2.     // do nothing   
  3. }  
 


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():

Java代码
  1. private native void helloNothing();   
  2. JNIEXPORT void JNICALL Java_com_taobao_pcache_jni_HelloNothing_helloNothing   
  3. (JNIEnv *env, jobject obj)   
  4. {   
  5.     // do nothing   
  6. }  
 


测试结果:
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被测试的方法:

Java代码
  1. private String helloString1(){   
  2.         return "hello world!";   
  3. }  
 


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被测试的方法:

Java代码
  1. JNIEXPORT jstring JNICALL Java_com_taobao_pcache_jni_HelloNothing_helloString   
  2. (JNIEnv *env , jobject obj)   
  3. {   
  4.     return (*env)->NewStringUTF(env, "hello world!");   
  5. }  
 


测试结果:
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原理 - winco - winco的博客

 
 
 
 
 评论这张
 
分享到:
评论

相关推荐

    JNI实现原理权威指南

    要深入理解JNI的实现原理,本书《JNI实现原理权威指南》无疑是提供了一个全面的技术指导和规范说明。 ### 知识点一:JNI的概念及其重要性 JNI是Java平台标准版的一部分,它为Java代码和本地应用程序接口(API)...

    JNI原理及实例解释

    **JNI原理** JNI的核心在于创建一个桥接,允许Java代码与本地代码交互。当Java虚拟机(JVM)遇到一个声明为`native`的方法时,它不会尝试解释执行该方法,而是通过JNI接口寻找对应的本地实现。这个本地实现通常是一...

    jni.zip jni编译jni下载

    同时,由于涉及到跨语言和跨平台,理解JNI的工作原理和正确使用JNI头文件至关重要,这样才能确保Java和本地代码之间的无缝交互。 在提供的压缩包`jni.zip`中,包括了`jni.h`和`jni_md.h`两个头文件,它们是进行JNI...

    jni开发demo:通过jni将一张彩色图片转换为黑白图片

    首先,我们需要了解JNI的基本工作原理。JNI主要包括以下几个步骤: 1. **创建本地库**:在Java代码中,我们需要声明一个native方法,该方法没有具体的实现,而是通过`native`关键字标记。例如: ```java public ...

    android使用JNI图片转黑白图片实现二值化

    在Android开发中,有时我们需要对图像进行高效处理,例如将彩色图片转换为黑白图片,或者进一步进行二值化处理。...通过学习和使用这个库,开发者不仅可以提升应用程序的性能,还能深入理解JNI和图像处理的基本原理。

    JNI详解和操作指南

    本文将深入解析JNI的概念、工作原理以及如何在实际项目中运用。 1. **JNI简介** JNI是Java平台的标准接口,它提供了一套规范,使得Java代码可以调用本地(Native)代码,也就是非Java语言编写如C、C++的代码。同时...

    jni4net-0.8.8.0.zip

    这个压缩包通常包含了该版本的所有必要的组件和文件,便于用户...然而,使用它需要对Java和.NET的互操作有一定了解,以及对JNI原理的熟悉。通过不断学习和实践,开发者可以充分利用jni4net带来的便利,提高开发效率。

    jni传递对象数组

    JNI,全称Java Native ...在实际开发中,理解JNI的工作原理和正确管理内存至关重要,以避免可能出现的内存泄漏和异常。同时,需要注意的是,频繁的JNI调用会增加应用程序的复杂性,并可能影响性能,因此应当适度使用。

    JNI CHM文档以及JNI编程规范

    通过深入学习和实践这些文档,开发者不仅能理解JNI的工作原理,还能熟练地编写混合Java和本地代码的应用程序。同时,理解和遵守JNI编程规范能确保代码的可维护性和健壮性,避免潜在的问题,如内存泄漏、线程安全问题...

    JNI

    `C++ GUI Programming with Qt 4.chm`可能就是一本关于如何使用C++和Qt创建GUI的参考资料,虽然没有直接提供关于JNI的细节,但它可以帮助理解C++和GUI编程的基本原理,这对于在Java中通过JNI调用Qt库是非常有帮助的...

    jni相关书籍资料

    JNI,全称Java Native Interface,是...通过阅读这些书籍,可以系统地理解JNI的工作原理,学习如何在Java和本地代码之间建立桥梁,从而在各种复杂场景下灵活地融合Java和C/C++的优点,实现高性能、跨平台的应用程序。

    Android开发书籍 - JNI详解-导航版

    * JNI 的设计原理:JNI 的设计原理是基于 Java 虚拟机和本地库之间的接口,允许 Java 代码调用本地代码,本地代码也可以调用 Java 代码。 * JNI 的实现细节:JNI 的实现细节包括了 Java 虚拟机和本地库之间的接口...

    JNI资料技术文档

    英文原版和中文版的"the java Native Interface"可以帮助开发者深入理解JNI的内部工作原理。 通过理解和掌握这些知识点,开发者可以有效地利用JNI和NDK进行Android应用的本地化开发,提高应用的性能和功能范围。

    android通过JNI访问硬件LED

    在Android系统中,Java Native Interface (JNI) 是一个关键组件,允许Java代码与其他语言(如C++或C)编写的代码进行交互...开发者可以通过学习和实践这个项目,深入了解Android系统的底层工作原理以及JNI的使用技巧。

    JNI详解中文版本

    ### JNI详解中文版知识点概述 ...然而,正确使用JNI需要深入了解其工作原理及潜在的风险点。对于那些希望充分利用本地代码性能优势或者希望实现多语言互操作性的项目来说,深入学习和掌握JNI将是必不可少的。

    android jni 屏幕截图 NativeScreenCapture

    2. **屏幕截图原理**:Android系统的屏幕截图通常涉及获取显示服务器的缓冲区数据。在JNI中,我们需要通过系统服务获取到SurfaceFlinger(Android图形系统的核心组件)的句柄,然后读取其缓冲区的内容。 3. **...

    jni层内存泄漏检测工具

    LeakTracer的工作原理通常包括以下几个步骤: 1. **对象追踪**:LeakTracer会监控JNI层创建的Java对象,记录它们的生命周期,包括创建、使用和释放过程。 2. **引用分析**:它检查JNI函数中是否存在对Java对象的...

    JniDemo_H246分析_DEMO_jni测试_

    本篇文章将通过分析“JniDemo_H246分析_DEMO_jni测试”项目,深入探讨JNI的基本概念、工作原理以及在实际应用中的关键步骤。 首先,我们需要理解JNI的核心概念。JNI为Java开发者提供了一种机制,使得Java代码能够...

    Android 深入研究JNI详解

    **2.1 JNI的工作原理概述** JNI作为Java平台的一个重要组成部分,允许Java代码与其他编程语言(如C、C++)编写的代码进行交互。在Android平台上,这意味着Java应用可以通过JNI与C/C++编写的.so库进行交互,以此提高...

    JNI编程指南书

    2. **自己实现Java虚拟机**:JNI是理解虚拟机工作原理的关键之一,特别是在实现垃圾回收器或多线程管理方面,了解JNI的工作方式对于构建自定义的JVM至关重要。 3. **学习不同语言之间的协作**:JNI不仅是Java与C/...

Global site tag (gtag.js) - Google Analytics