- 对本书的赞誉
- 前言
- 基础篇
- 第 1 章 Android 中锁屏密码加密算法分析
- 第 2 章 Android 中 NDK 开发
- 第 3 章 Android 中开发与逆向常用命令总结
- 第 4 章 so 文件格式解析
- 第 5 章 AndroidManifest.xml 文件格式解析
- 第 6 章 resource.arsc 文件格式解析
- 第 7 章 dex 文件格式解析
- 防护篇
- 第 8 章 Android 应用安全防护的基本策略
- 第 9 章 Android 中常用权限分析
- 第 10 章 Android 中的 run-as 命令
- 第 11 章 Android 中的 allowBackup 属性
- 第 12 章 Android 中的签名机制
- 第 13 章 Android 应用加固原理
- 第 14 章 Android 中的 so 加固原理
- 工具篇
- 第 15 章 Android 逆向分析基础
- 第 16 章 反编译神器 apktool 和 Jadx
- 第 17 章 Hook 神器 Xposed
- 第 18 章 脱壳神器 ZjDroid
- 第 19 章 Native 层 Hook 神器 Cydia Substrate
- 操作篇
- 第 20 章 静态方式逆向应用
- 第 21 章 动态调试 smali 源码
- 第 22 章 IDA 工具调试 so 源码
- 第 23 章 逆向加固应用
- 第 24 章 逆向应用经典案例分析
- 第 25 章 Android 中常见漏洞分析
- 第 26 章 文件加密病毒 Wannacry 样本分析
2.7 C/C++中的引用类型和 ID 的缓存
2.7.1 引用类型
从 Java 虚拟机创建的对象传到本地 C/C++代码时会产生引用,根据 Java 的垃圾回收机制,只要有引用存在就不会触发该引用所指的 Java 对象的垃圾回收。下面介绍 C/C++中的引用类型。
1.局部引用
局部引用是最常见的引用类型,基本上通过 JNI 返回来的引用都是局部引用,例如使用 NewObject 就会返回创建出来的实例的局部引用,局部引用只在该 native 函数中有效,所有在该函数中产生的局部引用,都会在函数返回的时候自动释放,也可以使用 DeleteLocalRef 函数手动释放该引用。那么,既然局部引用能够在函数返回时自动释放,为什么还需要 DeleteLocalRef 函数呢。
实际上局部引用存在是防止其指向的对象被垃圾回收,尤其是当一个局部引用指向一个很庞大的对象,或是在一个循环中生成了局部引用。最好的做法就是在使用完该对象后,在该循环尾部把这个引用释放掉,以确保在触发垃圾回收器的时候能够回收。
在局部引用的有效期中,可以传递到别的本地函数中,要强调的是它的有效期仍然只在一次的 Java 本地函数调用中,所以千万不能用 C++全局变量保存它或者把它定义为 C++静态局部变量。
2.全局引用
全局引用可以跨越当前线程,在多个 native 函数中有效,不过需要编程人员手动来释放该引用,全局引用存在期间会防止在 Java 的垃圾回收器的回收。
与局部引用不同,全局引用的创建不是由 JNI 自动创建的,全局引用是需要调用 NewGlobalRef 函数,而释放它需要使用 ReleaseGlobalRef 函数。
3.弱全局引用
弱全局引用是 Java 1.2 新出来的功能,与全局引用相似,创建和删除都需要由编程人员来进行,这种引用与全局引用一样可以在多个本地代码中有效,也跨越多线程有效。不一样的是,这种引用将不会阻止垃圾回收器回收这个引用所指向的对象,使用 NewWeakGlobalRef 和 ReleaseWeakGlobalRef 来产生和解除引用。
关于引用的一个函数如下:
上述的六种方法很好理解,这里就不做解释了。
这个函数是用来比较两个引用是否相等,但是对于弱全局引用还有一个特别的功能,如果把 NULL 传入要比较的对象中,就能够判断弱全局引用所指向的 Java 对象是否被回收。
缓存 jfieldID/jmethodID,取得 jfieldID 和 jmethodID 的时候会通过该属性/方法名称加上签名来查询相应的 jfieldID/jmethodID。这种查询相对来说开销大,我们可以将这些 FieldID/MethodID 缓存起来,这样就需要查询一次,以后就是用缓存起来的 FieldID/MethodID 了。
2.7.2 缓存方法
1.在用的时候缓存
在 native 代码中使用 static 局部变量来保存已经查询过的 id,这样就不会在每次函数调用时查询,而只要第一次查询成功后就保存起来了。不过在这种情况下就不得不考虑多线程同时调用此函数时可能会招致同时查询的危机,不过这种情况是无害的,因为查询同一个属性方法的 ID 通常返回的是一样的值:
static jfieldID fieldID_string=NULL;这段代码只执行一次。
2.在 Java 类初始化时缓存
更好的一个方式是在任何 native 函数调用前把 ID 全部存起来,可以让 Java 在第一次加载这个类的时候首先调用本地代码初始化所有的 jfieldID/jmethodID,这样就可以省去多次确定 ID 是否存在的语句。当然,这些 jfieldID/jmethodID 是定义在 C/C++的全局,使用这种方式还是有好处的,当 Java 类卸载或者重新加载的时候,也会调用该本地代码来重新计算 ID 的。
在 Java 中使用静态代码块进行初始化。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论