- 浏览: 1032040 次
- 性别:
- 来自: 杭州
-
文章分类
- 全部博客 (826)
- 硬件 (8)
- 软件 (24)
- 软件工程 (34)
- JAVA (229)
- C/C++/C# (77)
- JavaScript (8)
- PHP (1)
- Ruby (3)
- MySQL (14)
- 数据库 (19)
- 心情记事 (12)
- 团队管理 (19)
- Hadoop (1)
- spring (22)
- mybatis(ibatis) (7)
- tomcat (16)
- velocity (0)
- 系统架构 (6)
- JMX (8)
- proxool (1)
- 开发工具 (16)
- python (10)
- JVM (27)
- servlet (5)
- JMS (26)
- ant (2)
- 设计模式 (5)
- 智力题 (2)
- 面试题收集 (1)
- 孙子兵法 (16)
- 测试 (1)
- 数据结构 (7)
- 算法 (22)
- Android (11)
- 汽车驾驶 (1)
- lucene (1)
- memcache (12)
- 技术架构 (7)
- OTP-Erlang (7)
- memcached (17)
- redis (20)
- 浏览器插件 (3)
- sqlite (3)
- Heritrix (9)
- Java线程 (1)
- scala (0)
- Mina (6)
- 汇编 (2)
- Netty (15)
- libevent (0)
- CentOS (12)
- mongod (5)
- mac os (0)
最新评论
-
kingasdfg:
你这里面存在一个错误添加多个任务 应该是这样的 /** * ...
Quartz的任务的临时启动和暂停和恢复【转】 -
kyzeng:
纠正一个错误,long型对应的符号是J,不是L。
Jni中C++和Java的参数传递 -
zhaohaolin:
抱歉,兄弟,只是留下作记录,方便学习,如果觉得资料不好,可以到 ...
netty的个人使用心得【转】 -
cccoooccooco:
谢谢!自己一直以为虚机得使用网线才可以与主机连接呢。。
主机网卡无网线连接与虚拟机通信 -
yuqilin001:
要转别人的东西,请转清楚点嘛,少了这么多类,误人子弟
netty的个人使用心得【转】
简介: Java 本地编程接口(Java Native Interface,JNI) 易懂且容易上手,但对于一些新手甚至于一些中级开发者,在编程中都没有把 JNI 对象的生命周期考虑在内。以至于经常碰见莫名的 Crash,或者 segmentation fault。在花费了相当精力去调试后,才发现是 JNI 对象使用不当,当初保存的 JNI 对象已经被系统回收,却仍试图去使用之。本文结合实际开发中遇到的此类问题,讲述和强调了 JNI 对象生命周期的概念,并提出正确的解决方法,希望能和读者分享自己的经验和教训。
发布日期:
2007 年 12 月 06 日
级别:
中级
访问情况
1681 次浏览
建议:
0 (添加评论
)
在 JNI 编程中常需要从一个普通的 C/C++ 函数中调用 JNI 方法,比如:
long ProcessKeyboardEventInJava(int keyboardEvent) { long error = 0; JNIEnv* env = g_env; jclass that = g_class; jmethodID mid = g_mid; … … error = env->CallStaticIntMethod(that, mid, keyboardEvent); return error; } |
ProcessKeyboardEventInJava
只是个普通的 C 函数,目的是把参数中的键盘事件交给 Java 代码处理。它并没有 JNI 参数,却需要调用一个 JNI 方法。那么调用所必需的参数 JNIEnv
,jclass
和 jmethodID
,是如何获得呢?从上面的代码中可以看出,它们来自保存的全局变量 g_env
,g_class
和 g_mid
。这样做对么?安全么?也许这段代码在某种情况下,实现了软件的需求,成功地执行了 Java 的函数,处理了这个键盘事件。在这种情况下,设计者很清楚,也深信 ProcessKeyboardEventInJava
只会在某个 Native 函数返回前被调用。对应的场景代码应该是这样的:
JNIEnv * g_env; jclass g_class; jmethodID g_mid; JNIEXPORT jint JNICALL Java_SampleTest_init (JNIEnv *env, jclass cls) { g_class = cls; g_env = env; g_mid = env->GetStaticMethodID(cls, "ProcessKeyboardEventInJava", "(III)I"); while(true) { …. long error; error = ProcessKeyboardEventInJava(theEvent) …. } } |
但万一 ProcessKeyboardEventInJava
在 Java_SampleTest_init
(以下简称 init
)返回后的其他时机被调用了,会怎样呢?那时可就没有这么幸运了,初衷是肯定达不到了,而发生 crash 或者 segmentation fault 这类错误则几乎是一定的。事实上,如上设计,ProcessKeyboardEventInJava
也只允许在 init
函数中被调用。在其返回后被调用,就不合法了。所以,如果你是这个设计师,之前的设计中,是不是这个假设也有些太乐观了呢?
调用者本来以为这个函数能帮他的大忙,现在却疑惑了,到底发生了什么?做错了什么呢?
原因其实很简单,上面三个全局变量中的 g_class
已经是非法的了,生命周期在退出 init
之后就结束了。
这里要澄清的是 g_mid
,由于它不是一个 jobject,所以只要它对应的 class 没有被卸载,在退出 init
之后仍可以使用,没问题;而 g_env
对于同一个 thread 来说,它是唯一的,所以只要是 init
和 ProcessKeyboardEventInJava
处于一个 thread,初始化后它的值在这个 thread 没有中止之前,都一直是合法的。
那如何才能解决 g_class
的非法引用带来的问题呢?
这首先涉及到 Java 和 Native 代码之间函数调用时,参数如何传递的问题。简单类型,也就是内置类型,比如 int, char 等是值传递(pass by value),而其它 Java 对象都是引用传递(pass by reference),这些对象引用由 JVM 传给 Native 代码,每个都有其生命周期。
其次,Java 对象的生命周期是由它的引用类型决定的,这里的引用分两种:local reference 和 global reference。Native 函数参数中 jobject 或者它的子类,其参数都是 local reference。Local reference 只在这个 Native 函数中有效,Native 函数返回后,引用的对象就被释放,它的生命周期就结束了。若要留着日后使用,则需根据这个 local reference 创建 global reference。Global reference 不会被系统自动释放,它仅当被程序明确调用 DeleteGlobalReference 时才被回收。
于是解决的办法就出来了:
在 init
函数中,不是简单地把 jcalss
参数保存,而是:
JNIEXPORT jint JNICALL Java_SampleTest_init (JNIEnv *env, jclass cls) { …. g_class= (jclass)(env->NewGlobalRef(cls)); …. } |
这样,无论 ProcessKeyboardEventInJava
是在 init
返回前还是返回后,调用它都是安全的,可行的了。
若要在某个 Native 代码返回后,还希望能继续使用 JVM 提供的参数(比如 init 函数中的 jclass), 或者是过程中调用 JNI 函数的返回值(比如 g_mid),只要它是一个 jobject, 则需要为它创建一个 global reference,以后只能使用这个 global reference;若不是一个 jobject,则无需这么做。
jclass 是由 jobject public 继承而来的子类,所以它当然是一个 jobject,需要创建一个 global reference 以便日后使用。而 jmethodID/jfieldID 与 jobject 没有继承关系,它不是一个 jobject,只是个整数,所以不存在被释放与否的问题,可保存后直接使用。JNIEnv 对于每个 thread 而言是唯一的,不能也不需要对它调用 NewGlobalReference。
学习
- “Global and Local References
”:Sun Java 文档中关于 JNI 的规范说明 。
- “Linux on POWER 的 JNI 编程实例
”(developerWorks,2005 年 3 月):通过一些简化的示例描述了重要的 Java 本地接口(Java Native Interface,JNI)编程概念。
- “在 Linux 平台下使用 JNI
”(developerWorks,2002 年 10 月):简要介绍了 JNI 调用规范,及常用函数。
- “教程:用 JNI 进行 Java 编程
”(developerWorks,2002 年 6 月):介绍了 JNI 编程的基础知识。
-
developerWorks
:数百篇关于 Java 编程各方面的文章。
讨论
-
developerWorks blogs
:加入 developerWorks 社区。
发表评论
-
调试jdk中的源码,查看jdk局部变量
2013-06-15 23:30 1066调试jdk中的源码,查看jdk局部变量 2012-04 ... -
Eclipse快捷键 10个最有用的快捷键<转>
2013-04-11 23:28 1092Eclipse中10个最有用的快捷键组合 一个Eclip ... -
Lucene 3.6 中文分词、分页查询、高亮显示等
2012-12-09 23:35 18491、准备工作 下载lucene 3.6.1 : htt ... -
Maven实战(九)——打包的技巧(转)
2012-10-12 00:41 950“打包“这个词听起 ... -
基于Maven的web工程如何配置嵌入式Jetty Server开发调试环境(转)
2012-10-12 00:28 9661、首先在web工程的POM文件里添加依赖jar包如下: ... -
轻轻松松学Solr(1)--概述及安装[转]
2012-09-18 14:59 1006概述 这段时间对企 ... -
分析Netty工作流程[转]
2012-09-04 19:02 911下面以Netty中Echo的例 ... -
让eclipse在ubuntu下面好看一点
2012-03-27 10:17 940<p> </p> <h1 cla ... -
zookeeper安装和应用场合(名字,配置,锁,队列,集群管理)[转]
2012-01-12 17:59 1666安装和配置详解 本文 ... -
Jakarta-Common-BeanUtils使用笔记[转]
2012-01-10 14:13 1171Jakarta-Common-BeanUtils ... -
一个关于Java Thread wait(),notify()的实用例【转】
2012-01-07 16:05 1036///// // ProducerConsume ... -
Java基础:Java中的 assert 关键字解析【转】
2012-01-06 19:50 1080J2SE 1.4在语言上提供了 ... -
一篇不错的讲解Java异常的文章(转载)----感觉很不错,读了以后很有启发[转]
2012-01-06 15:02 1285六种异常处理的陋习 ... -
如何解决HP QC(Quality Center)在Windows 7下不能工作的问题
2011-12-26 10:48 1607HP QC(Quantity Center) 是一款不错的测 ... -
JAVA读写文件,中文乱码 【转】
2011-12-19 23:43 2138最近在做HTML静态生成,需要从硬盘上把模版文件的内容读出来。 ... -
Java 6 JVM参数选项大全(中文版)【转】
2011-12-19 19:51 986Java 6 JVM参数选项大全(中文版) 作者 ... -
使用assembly plugin实现自定义打包【转】
2011-12-13 01:58 987在上一篇文章中,讨论到在对maven的机制不熟悉的情况下,为了 ... -
使用maven ant task实现非标准打包[转]
2011-12-13 01:56 1062maven很强大,但是总有些事情干起来不是得心应手,没有使用a ... -
Java日期转换SimpleDateFormat格式大全【转】
2011-12-08 20:22 133224小时制时间 显示: public clas ... -
使用Spring的表单标签库
2011-11-22 20:08 110113.9. 使用Spring的 ...
相关推荐
4. **实现回调**:在C/C++代码中,当需要触发回调时,使用`CallVoidMethod`等函数调用Java接口的相应方法。 **注意事项** 1. **线程安全**:Java和C/C++运行在不同的线程中,因此在多线程环境下,需要处理好同步...
然后,你需要在JNI中定义一个函数,接收Java对象作为参数,处理后返回自定义对象。这通常涉及到JNI的`NewObject`函数,用于创建Java对象。例如: ```cpp jobject createCustomObject(JNIEnv *env, int id, const ...
1. **声明JNI函数**:在C/C++头文件中,我们需要声明一个JNI函数,该函数接受`JNIEnv`指针(提供访问Java虚拟机和Java对象的方法)以及`jobject`参数。 ```c JNIEXPORT void JNICALL Java_Package_Name_MethodName...
- 由于JNI的内存管理规则,需要特别注意对象的生命周期和内存释放。 - 如果DLL是Delphi编写的,需要注意Delphi的字符串表示和Java字符串之间的转换。 通过以上步骤,你就可以成功地在Java程序中通过JNI调用C++和...
- `NewGlobalRef`/`DeleteGlobalRef`:创建和释放全局引用,用于保持Java对象在本地方法中的生命周期。 - `CallVoidMethod`/`CallIntMethod`等:调用Java方法,根据返回类型选择不同的函数。 此外,JNI还支持处理...
如果在C++中运行,可能需要通过JNI的`AttachCurrentThread`等函数手动管理Java虚拟机(JVM)的生命周期。 总的来说,通过JNI,C++和Java代码可以相互调用,提供了一种灵活的跨语言通信机制。在Visual Studio 2019中...
在C语言中,调用JNI函数时必须使用`(*env)->`语法,而在C++中可以简化为`env->`。这是由于C++的名称修饰(name mangling)规则导致的,C++会改变函数的名称以处理函数重载,而C语言没有这种机制。 7. **字符串处理...
当在JNI函数中声明接收这些类型时,需要使用这些特定的JNI类型。例如,以下示例展示了如何在JNI中接收并处理一个int参数: ```c JNIEXPORT void JNICALL Java_com_example_MyClass_doSomething(JNIEnv *env, ...
Java对象的引用在JNI层必须使用`GlobalRef`或`LocalRef`来保持其生命周期,防止垃圾回收。 6. **编译和链接**: - 编译C++代码为动态链接库(DLL),确保链接到Java运行时库(如libjvm.dll)。 - 将DLL库放在Java...
- **内存管理**:注意在C/C++中使用Java对象时的生命周期管理,因为它们的生命周期由JVM控制,而非C/C++的堆栈或动态分配。 通过这个"android jni 传递对象的demo",开发者可以深入理解Java和C/C++之间的数据交互,...
在实际开发中,Delphi程序员需要理解JNI的生命周期管理,比如如何正确地创建和释放本地引用,以防止Java对象过早被垃圾回收。此外,还要注意线程安全问题,因为Java是多线程的,而Delphi的代码可能需要同步控制以...
需要注意的是,对象的生命周期管理,例如,使用`GlobalRef`和`LocalRef`来管理对象引用,防止过早回收。 这个"hello-jni"示例可能包含了这些操作的实践,通过分析源代码,我们可以更深入地理解JNI的工作原理和使用...
然而,操作Java对象的过程中还涉及到了其他重要概念,如异常处理、签名规范、对象的生命周期管理等。这些主题将在后续的文章中进一步探讨。 JNI的灵活性和强大性使其成为跨语言编程的重要工具,尤其是在需要高性能...
此外,JNI提供了引用计数机制,用于管理Java对象在本地代码中的生命周期。 7. **错误处理和调试**: JNI编程中,错误处理至关重要。由于本地代码和Java代码是分开的,因此需要良好的日志记录和调试手段。Android ...
4. **通信接口设计**:为了在Qt和Java之间传递数据,可以创建自定义的数据结构,如JNI局部引用、全局引用或弱全局引用,以管理对象生命周期。 5. **加载动态库**:在Android的Activity或Service中,使用System....
2. 全局和局部引用:JNI提供了全局引用和局部引用来管理Java对象的生命周期,防止垃圾回收器过早回收被本地代码使用的对象。 三、调用Java方法 1. 直接方法调用:使用`CallXXXMethod`系列函数调用非静态Java方法,`...
局部引用(Local References)只在本地方法调用的生命周期内有效。 9. **线程支持**:JNI支持多线程环境,每个Java线程都有自己的JNIEnv指针。在本地代码中创建线程时,需要注意线程安全问题。 10. **动态链接库...
注意,由于内存管理在不同语言间可能存在差异,所以必须手动管理C++对象的生命周期,防止内存泄漏。 在完成C++的实现后,我们需要编译这个C++源文件为动态链接库,然后在Java中加载这个库,这样Java就可以通过JNI...
在Android的JNI(Java Native Interface)开发中,`NewGlobalRef`是一个至关重要的函数,它允许我们在本地C/C++代码中创建对Java对象的全局引用。这个知识点涉及到Android应用的跨语言交互,内存管理和生命周期管理。 ...
9. **全局和局部引用**:JNI提供了全局引用(`NewGlobalRef`)和局部引用(`NewLocalRef`)的概念,以管理Java对象的生命周期。全局引用不会因为本地方法返回而自动释放,而局部引用则会在方法返回时被自动清理。 ...