JNI浅谈

分散知识点:

每一个进程对应一个Runtime,每一个Runtime对应一个JavaVMExt(JavaVM),是ART虚拟机中全局唯一的虚拟机代表,可以通过Runtime::GetJavaVM函数获取。

每个线程对应一个tlsPtr结构体,tlsPtr中的pthread_self代表一个线程,每个线程都有一个JNIEnvExt(JavaEnv)。

根据JNI规范,目标动态库必须和一个ClassLoader对象相关联,同一个动态库不能由不同的ClassLoader对象加载

JavaVM:
  • LoadNativeLibrary:(加载动态库)
    1.先从Map(libraries_)中查找是否加载过这个动态库,加载过,还需要判断之前加载的ClassLoader与当前方法传入的ClassLoader是否为同一个,是则返回true,否则返回false。没有加载过则执行第2步;
    2.调用OpenNativeLibrary加载动态库,该方法是Android系统的定制版本,出于安全考虑,一个应用不能加载另一个应用携带的动态库。
    3.通过handle句柄,找到JNI_Onload函数,将当前JavaVM作为参数传入,返回非JNI_ERR,则加载动态库成功。JNI_Onload函数的调用成功与否,动态库都已经加载到虚拟机内存,失败也不会卸载这个动态库。

  • FindCodeForNativeMethod:(将Java native方法与动态库的方法关联起来)
    根据参数ArtMethod获取Java native方法的签名信息,遍历Map(libraries)找到符合条件的函数指针void *。

  • globals_和weak_globals_两个IndirectReferenceTable(IRTable)成员变量来管理JNI层中的Global型和WeakGlobal型引用对象。

JavaEnv:
  • locals成员变量是IRTable类型数据结构,用来管理每个Java线程因JNI调用而创建的Local型引用对象。
    JNI方法返回后这些Local型引用对象就被释放,释放是指设置持有它们的变量的值为null,而不是回收,回收时GC模块进行处理的。

  • PushLocalFrame和PopLocalFrame:
    JavaEnv的locals IRTable只能存储512个Local型引用对象。当特定场景下,需要更多的容量,则使用这两个函数。
    PushLocalFrame:压入一个栈帧,后续的NewObject将在这个栈帧对应的资源中分配;
    PopLocalFrame:退出一个栈帧,这个栈帧上分配的资源将被释放。
    注意:可以多次Push和Pop,但是要一一对应。

有关快速JNI调用的注意事项:

在普通的JNI调用中,调用线程通常从kRunnable状态转换为kNative状态。但是,如果被调用的本机函数需要访问任何Java对象,则必须转换回kRunnable状态。

双重转换会产生成本。对于应该快速的JNI呼叫,此成本可能会占主导地位。

在快速JNI调用上,调用线程通过不从kRunnable过渡到kNative并保持在kRunnable状态来避免这种双重过渡。

使用快速JNI调用存在风险,因为它可能会延迟对线程挂起请求的响应,该请求通常用于GC根扫描等。如果快速JNI调用花费的时间较长,则可能导致更长的线程挂起延迟和GC暂停。

因此,应谨慎使用快速JNI。它应用于需要很短时间(例如,没有长时间运行的循环)并且不会阻塞(例如,没有锁,I / O等)的JNI调用。
一种 ‘!’ JNINativeMethod中的签名中的前缀表示这是一个快速的JNI调用,并且运行时在入口处忽略了从kRunnable到kNative的线程状态转换。

问题:
1.注册native方法的原理?

在动态库中的JNI_Onload方法中进行注册,JNI_Onload在动态库加载的时候会调用(JavaVM::LoadNativeLibrary)。
动态注册:会根据Java方法名和方法签名找到对应的ArtMethod,然后通过ArtMethod::RegisterNative将C、C++函数指针与ArtMethod绑定。
静态注册:静态注册会被动态注册覆盖,如果没有动态注册,则根据Java native方法对应的类的包名和函数生成字符串(这个字符串就是静态注册的函数的名字),以及Java native方法的函数签名,在动态库中找到与之对应的函数,然后调用。

2.jint、jlong等基本数据是如何跟java语言中的int、long等数据进行传递的?

jint、jlong等基本数据的大小与Java的基本数据int、long等大小相同。在ndk调用java的方法时,jmethod* 实际上就是ArtMethod*,在调用过程中,将大于jint的数据按照两个jint大小存入int32数组中,将小于等于jint大小的按照一个jint大小存入int32数组,最终通过ArtMethod::invoke()将int数组和数组长度作为参数传入。

3.java方法与ndk方法是如何对接的?

中间的桥梁就是函数名和函数签名,也是ArtMethod和native函数指针的对接。
参数是引用类型对象,则使用mirror::Object来对接,每个Java对象对应一个mirror::Object。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值