项目场景:
在jna调用so文件或者在win环境下调用dll找不到函数名,本次是个人调用jna执行so动态库或者dll的时候真实遇到的问题,做个记录
问题描述
jna在调用so和dll的时候,因为java是跨平台的他会动态的去读取系统的的信息,所以做双平台的代码书写很有必要,我大概讲一下会遇到哪些问题吧,
问题1 :java程序读取不到so文件,找不到目录,在linux下so文件编译出来之后需要将so动态库放入/usr/lib64目录下,如果不放在这个目录就需要自己去配置环境变量去指定你的库位置
解释:linux下so文件就是可执行的动态库,so文件依赖的一些文件也需要放在同级目录下,否则会读取不到,特别注意,有些qt编译器编译出来的一些文件so放在/usr/lib64下,但是so依赖的文件需要放在tomcat的bin目录下才能被动态库读取到,这个应该是启动的位置关系
1.在终端输入:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/XXX 只在当前终端起作用
2. 修改~/.bashrc或~/.bash_profile,最后一行添加 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/XXX,保存之后,使用source .bashrc执行该文件 ,当前用户生效
3. 修改/etc/profile,添加内容如第2条,同样保存之后使用source执行该文件 所有用户生效
问题2:在调用的时候报错找不到对应的函数方法,实际上我写的函数名映射和c里面写的一样,但是经过编译之后,dll和so文件里面实际的函数名称会有变化这时候可以用反编译工具去看里面的实际函数方法,然后在jna映射函数方法名的时候去更改,
nm 是一个在 Unix-like 操作系统(如 Linux 和 macOS)上用于列出目标文件(object files)、库文件(如 .so 文件)和可执行文件中的符号表内容的工具。然而,nm 工具本身并不直接支持 Windows 平台上的 DLL 文件。对于 .so 文件(Linux 上的共享库)和 DLL 文件(Windows 上的动态链接库),你需要使用不同的方法来查看它们包含的符号。
查看 .so 文件中的函数名称
在 Linux 上,你可以使用 nm 工具来查看 .so 文件中的函数名称。以下是一个简单的示例:
sh
nm -D libexample.so
这里的 -D 选项表示显示动态符号。其他常用选项包括:
-C:对 C++ 代码进行 demangle(去除函数名中的修饰符,使其更易读)。
-u:显示未定义的符号。
例如:
sh
nm -DC libexample.so
查看 DLL 文件中的函数名称
在 Windows 上,nm 工具不适用。你可以使用 dumpbin 工具(随 Visual Studio 一起提供)或 Dependency Walker 等工具来查看 DLL 文件中的函数名称。
使用 dumpbin
dumpbin 是一个命令行工具,可以显示 Windows 可执行文件和对象文件的各种信息。以下是一个使用 dumpbin 查看 DLL 文件中导出函数的示例:
sh
dumpbin /exports yourlibrary.dll
这将列出 DLL 文件中的所有导出函数及其地址。
使用 Dependency Walker
Dependency Walker 是一个图形界面工具,用于查看 Windows 可执行文件和 DLL 文件的依赖关系和导出函数。你可以下载并运行 Dependency Walker,然后加载你的 DLL 文件,它将以树状结构显示所有导出的函数。
jna调用内存溢出问题:
在java中没有内存对齐的问题,但是so文件或者dll文件就会有这个问题
内存溢出这个个问题一般是so文件里面的代码书写规范,或者内存越界了,比如一个两个double数组在相互赋值的时候有个数据给付错了,执行的时候不会报错,但是在执行完释放内存的时候就会爆出oom内存溢出或者内存越界,这类问题就得逐步逐步去排查,根据报错去看,比如在java报错日志中报出double越界的问题就去c代码中找double的一些赋值状况,什么类型报错就去找什么
以下是我写得一个jna加载的模版,注意事项也在里面可以复制进自己的代码改成自己的应用库执行
public class JNAUtil {
// customization and mapping of Java to native types.
public interface CLibrary extends Library {
// 函数与java的默认映射 传入结构体的话得按jna 目录下PalletLocStruct的去设计
int printf(String format, Object... args);
int printf结构体(PalletLocStruct palletLocStruct);
}
// 手动去修改映射的函数方法名称。此为java-c 两者映射的函数名称修改
static CLibrary loadJNA() {
// 使用lambda表达式创建FunctionMapper的实例
FunctionMapper functionMapper = new FunctionMapper() {
@Override
public String getFunctionName(NativeLibrary library, Method method) {
System.out.println(method.getName());
return functionNameMap.getOrDefault(method.getName(), method.getName());
}
private final Map<String, String> functionNameMap = new HashMap<>();
{
// 初始化映射关系根据实际的函数名称去写,因为有可能会变化
if (Platform.isWindows())
functionNameMap.put("myJavaMethodNameWindows", "actualLibraryNameInDLL");
else functionNameMap.put("myJavaMethodNameForLinux", "actualLibraryNameInSo");
}
};
// load:根据windows和linux的动态库名称区分去写 ,注意dll和so文件调用的函数方法名称可能不一样
return (CLibrary) Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class, (Map<String, ?>) functionMapper);
}
public static void main(String[] args) {
// 注意数组类型等一些集合可以传入c里面进行数据交换,这样就可以获取到多个返回参数,也可让返回json,特别注意数组需要创建定长数组,否则c会因内存对齐问题报错
JNAUtil.loadJNA().printf("aaa");
JNAUtil.loadJNA().printf结构体(new PalletLocStruct());
}
}
以下是结构体的代码及其说明配合上面的结构体使用
---
//1:使用FieldOrder注解标注结构体成员的顺序,切记顺序一定不能弄错,
//不然结构体取出来的值会路透不对马嘴,因为结构体成员在内存中是依次排列的,
//如果顺序弄错jvm照着这个顺序按成员变量的类型取值肯定会错了
@Structure.FieldOrder(value= {"beginLoc","confidence","x","y","z","theta"})
public class PalletLocStruct extends Structure {
//2:这个地方必须 public 不能private
public int beginLoc;
public float confidence = 1;
public int x = 0;
public int y = 0;
public int z = 0;
public float theta = 0;
//3:这个地方具体原理不知道,只知道声明之后,当前结构体可以按照引用或者值取值
public static class ByReference extends PalletLocStruct implements Structure.ByReference {}
public static class ByValue extends PalletLocStruct implements Structure.ByValue{}
//4:这个地方表示当前结构体是内存对齐的,因为c/c++ 的结构体成员多数情况是内存对齐,
//可以想象如果c/c++内存对齐,而java没有内存对齐,那取出来的值肯定也对不上
public PalletLocStruct() {
super(ALIGN_NONE);
}
public int getBeginLoc() {
return beginLoc;
}
public void setBeginLoc(int beginLoc) {
this.beginLoc = beginLoc;
}
public float getConfidence() {
return confidence;
}
public void setConfidence(float confidence) {
this.confidence = confidence;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
public float getTheta() {
return theta;
}
public void setTheta(float theta) {
this.theta = theta;
}