JNI 面试题及答案
JNI 基础概念
问题:什么是 JNI?它的作用是什么?
JNI(Java Native Interface)是 Java 本地接口,用于实现 Java 代码与其他语言(如 C/C++)的交互。它允许 Java 程序调用本地代码(Native Code),反之亦然。JNI 常用于性能优化、访问硬件或操作系统特性、复用现有本地库等场景。
问题:JNI 的优缺点是什么?
优点:
- 可以调用本地代码,提升性能。
- 访问系统级功能或硬件特性。
- 复用已有的 C/C++ 库。
缺点:
- 增加了复杂性,跨语言调用容易出错。
- 破坏了 Java 的跨平台性。
- 内存管理需手动处理,容易引发内存泄漏或崩溃。
JNI 开发流程
问题:如何实现一个简单的 JNI 调用?
-
在 Java 类中声明
native方法:javapublic class NativeDemo { public native void sayHello(); static { System.loadLibrary("NativeDemo"); } } -
使用
javac编译 Java 文件,生成.class文件。 -
使用
javah(或javac -h)生成头文件(.h),包含 JNI 方法签名。 -
实现 C/C++ 代码,包含头文件并实现方法:
c#include <jni.h> #include "NativeDemo.h" JNIEXPORT void JNICALL Java_NativeDemo_sayHello(JNIEnv *env, jobject obj) { printf("Hello from JNI!\n"); } -
编译为动态库(
.so或.dll),并确保 Java 程序能加载该库。
JNI 数据类型与内存管理
问题:JNI 中如何表示 Java 的基本数据类型?
JNI 定义了与 Java 基本类型对应的本地类型:
jint(对应int)、jlong(对应long)jfloat(对应float)、jdouble(对应double)jboolean(对应boolean)、jchar(对应char)
问题:JNI 如何处理 Java 对象?
通过 jobject 类型表示 Java 对象。
- 局部引用(Local Reference):默认创建,需手动释放或依赖 JNI 环境自动回收。
- 全局引用(Global Reference):显式创建(
NewGlobalRef),需手动释放。 - 弱全局引用(Weak Global Reference):不阻止垃圾回收,需检查是否有效。
JNI 异常处理
问题:JNI 中如何捕获和处理 Java 异常?
- 检查异常:调用 JNI 函数后,使用
ExceptionCheck或ExceptionOccurred检查是否有异常。 - 抛出异常:通过
ThrowNew抛出新的异常。 - 清除异常:使用
ExceptionClear清除当前异常。
示例代码:
c
jclass clazz = (*env)->FindClass(env, "java/lang/NullPointerException");
if (clazz != NULL) {
(*env)->ThrowNew(env, clazz, "Null pointer in JNI");
}
JNI 性能优化
问题:如何优化 JNI 调用的性能?
- 减少 JNI 调用次数:批量传递数据而非频繁调用。
- 使用局部引用缓存:避免重复查找类或方法 ID。
- 直接访问缓冲区:如
GetPrimitiveArrayCritical或NewDirectByteBuffer。 - 避免跨线程调用:JNIEnv 是线程相关的,需正确处理线程绑定。
JNI 线程安全
问题:JNI 如何实现多线程安全?
- 每个线程需通过
AttachCurrentThread获取自己的JNIEnv。 - 全局引用是线程安全的,但需注意同步问题。
- 避免在本地代码中长时间持有对象引用,防止内存泄漏。
示例代码:
c
JavaVM *jvm; // 全局变量
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
// 调用 JNI 方法
(*jvm)->DetachCurrentThread(jvm);
JNI 常见问题
问题:JNI 调用时出现 UnsatisfiedLinkError 的可能原因?
- 动态库未正确加载:路径错误或库名不匹配。
- 方法签名不匹配:头文件中的函数名或参数与实现不一致。
- 库依赖缺失:本地库依赖的其他库未正确部署。
问题:如何调试 JNI 代码?
- 使用
printf或日志输出调试信息。 - 通过
gdb或lldb调试本地代码。 - 检查
adb logcat输出的 Java 异常信息。
以上是常见的 JNI 面试题及答案,涵盖基础概念、开发流程、数据类型、异常处理、性能优化和线程安全等关键点。