JNI 常见异常分析

JNI基础内容:

编程语言间的互相调用可以有几种方案:

  • 通过RPC调用来实现跨语言的调用,不同语言编写的程序运行在不同的进程中,通过网络、输入输出流、管道等设施。
  • 通过共享内存实现进程间通信
  • 通过FFI(Foreign Function Interface)机制,通过提供运行时兼容的设施(一般是lib及运行时支持)实现在同一进程内跨越语言的边界调用函数,例如Java的JNI就是这样一种设施

JNI 技术背景:

运行环境:

基本数据类型:

引用数据类型:

方法签名:

Java/C 如何相互调用:

方法签名:

Jvm/JniEvn环境:

JNIEnv一般是是由虚拟机传入,而且与线程相关的变量,线程能操作的线程上下文 与 方法

JavaVM是虚拟机在JNI中的表示,一个JVM中只有一个JavaVM实例,这个实例是线程共享的

So 异常分析:

bash 复制代码
java.lang.UnsatisfiedLinkError: dlopen failed: library "/Users/user/Soft/Custom/dianxin/MH_ICT/app/src/main/cpp/lib/arm64-v8a/libcjson.so" not found: needed by /data/app/~~-fvxvo2CY1xGw_Zq_z_xjw==/com.dss.ict-tzmJw185jIwlMorqbPpxpw==/lib/arm64/libictProxy.so in namespace classloader-namespace

/Users/user/Library/Android/sdk/ndk/21.4.7075529/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin/x86_64-linux-android-readelf -d /Users/user/Downloads/app-general-debug/lib/arm64-v8a/libictProxy.so

方法异常问题 :

% objdump -T ./libonnx_decoder.so | grep "OrtGetApiBase"0000000000000000 DF UND 0000000000000000 (VERS_1.12.0) OrtGetApiBase

% objdump -T ./libonnxruntime.so | grep "OrtGetApiBase" 000000000045c51c g DF .text 000000000000000c VERS_1.19.0 OrtGetApiBase

so name 问题:

正常so

系统so 加载错误问题:

系统权限应用加载:

安卓默认会按照优先级搜索下面的路径:

  • 应用的安装目录如上面的(/data/app/xxx/lib/arm64/)
  • so文件的RPATH字段指的的目录
  • LD_LIBRARY_PATH环境变量指定的目录
  • so文件的RUNPATH字段指的的目录
  • 系统目录如/system/lib64/、/vendor/lib64/、/system/apex/com.android.i18n/lib64/等

加载后缀问题:

bash 复制代码
java.lang.UnsatisfiedLinkError: dlopen failed: library "libcurl.so.4" not found: needed by /data/app/~~gCIOmXUrd8VmBqA43J-WWw==/com.dss.ict-375hPFYMdMCuKeYl4ymVAg==/lib/arm64/libictProxy.so in namespace classloader-namespace

安卓系统是支持这种加载带版本后缀的so,但是gradle在编译apk的时候确是只会将.so后缀的文件打包到apk,所以安装之后就缺失了这个so。

在Android上库不是在系统范围内安装的它们总是应用程序包的一部分,所以so的版本标记是不必要的,谷歌就把这块在打包的时候去掉了,但这样的差异造成了在安卓上引入第三方c/c++库方面需要对so的版本号进行额外的处理。

开发过程问题

线程问题:

因为通过AttachCurrentThread附加到虚拟机 的线程在查找类时只会通过系统类加载器进行查找,不会通过应用类加载器进行查找,因此可以加载系统类,但是不能加载非系统类

引用数量问题:

lua 复制代码
JNI ERROR (app bug): local reference table overflow (max=512)

1 在某个本地方法调用中,创建的局部引用的数量不宜过多,否则可能导致JNI内部的局部引用表溢出,应该适时释放

2 不再使用的局部引用。尽管在本地方法返回后,局部引用会被自动释放,但是在这之前若不主动释放,局部引用所引用的对象会一直存活,可能导致使用的内存在方法调用时居高不下,甚至可能出现OOM的风险。

3 当一个本地方法不再返回,也就是内部无限循环执行,此时适时释放无用局部引用是必须的。例如,一个本地方法可能进入一个无终止的事件派遣循环。释放在循环中创建的局部引用至关重要(crucial),因此不会让引用数量累增而导致内存泄露

4 局部引用引用了一个较大的对象。在本地方法调用时,可能会访问java中大对象,局部引用在被释放之前会一直阻止JVM回收这个大对象,即是之后不再使用该对象,造成了内存浪费,因此适时释放该局部引用是合理的选择。 使用NewLocalRef/ DeleteLocalRef 来使用局部引用

相关推荐
EnCi Zheng10 分钟前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen14 分钟前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技15 分钟前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人26 分钟前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实27 分钟前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha37 分钟前
三目运算符
linux·服务器·前端
晓晨的博客1 小时前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect1 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript
donecoding1 小时前
别再让 pnpm 跟着 nvm 跑了!独立安装终极指南
前端·node.js·前端工程化
GISer_Jing1 小时前
AI全栈转型_TS后端学习路线
前端·人工智能·后端·学习