java使用jna调用go代码

调用流程

在 Java 中使用jna调用 Go 的大致过程如下

java -> jna -> c -> cgo -> go

整个过程要解决的问题主要两个

  • 数据类型在两种语言中如何转化
  • 内存的释放

示例代码

以下是简单的示例,并解决了内存泄露的问题

Go代码

//export add
func add(a, b C.int) C.int {
	aGo := int(a)
	bGo := int(b)
	res := aGo + bGo
	return C.int(res)
}

//export hello
func hello(name *C.char) *C.char {
	data := C.GoString(name)
	cs := C.CString("Hello " + data + "!")
	return cs
}

//export freePoint
func freePoint(cs *C.void) {
	C.free(unsafe.Pointer(cs))
}

func main() {}

Go代码编译为共享库

windows环境编译

使用windows 10的操作系统,gcc使用TDM-GCC-64

windows 64bit 共享库
go build -buildmode=c-shared -o ../resources/win32-x86-64/awesome.dll awesome.go
windows 32bit 共享库
set CGO_ENABLED=1
set GOARCH=386
go build -buildmode=c-shared -o ../resources/win32-i686/awesome.dll awesome.go

linux x86 环境编译

使用ubuntu操作系统

linux 64bit 共享库
GOARCH=amd64 GOOS=linux CGO_ENABLED=1 go build -buildmode=c-shared -o ../resources/linux-x86-64/libawesome.so awesome.go

linux arm64 环境编译

使用ubuntu操作系统
交叉编译器:gcc-8-aarch64-linux-gnu

linux 64bit 共享库
CC=aarch64-linux-gnu-gcc-8 GOARCH=arm64 GOOS=linux CGO_ENABLED=1 go build -buildmode=c-shared -o ../resources/linux-aarch64/libawesome.so awesome.go

JNA的动态库加载方式

将go打包生成的dll、so或dylib放在java的resources 下的{OS}-{ARCH}/{LIBRARY} 目录内

resources/
|── linux-aarch64
|         └── libawesome.dylib
├── linux-x86-64
│         └── libawesome.so
├── win32-x86-64
│         └── awesome.dll
├── win32-i686
│         └── awesome.dll
├── darwin
│      └── libawesome.dylib

JAVA代码

在pom.xml中引入jna

        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.6.0</version>
        </dependency>

共享库接口定义

public interface AuthLibrary extends Library {

    AuthLibrary INSTANCE = Native.load("awesome", AuthLibrary.class);

    int add(int a, int b);

    Pointer hello(Pointer name);

    void freePoint(Pointer pointer);
}

接口封装

public class AuthServer {
      public static String hello(String name) {
        // 申请jna的内存空间
        Pointer p = new Memory(name.getBytes(StandardCharsets.UTF_8).length + 1);
        // 设置传入参数值
        p.setString(0, name);
        Pointer ptr = null;
        try {
            ptr = AuthLibrary.INSTANCE.hello(p);
            return ptr.getString(0, "utf8");
        } finally {
            // 释放传入jna的指针对应的内存空间
            Native.free(Pointer.nativeValue(p));
            // 解决多次调用崩溃的问题
            Pointer.nativeValue(p, 0);
            if (ptr != null) {
                // 释放go中申请的C的内存
                AuthLibrary.INSTANCE.freePoint(ptr);
            }
        }
    }
    
    private static void freeJna(Pointer pointer) {
        // 释放传入jna的指针对应的内存空间
        Native.free(Pointer.nativeValue(pointer));
        // 解决多次调用崩溃的问题
        Pointer.nativeValue(pointer, 0);
    }
}

测试代码

public class JNATest {

    public static void main(String[] args) {
        System.out.println(AuthLibrary.INSTANCE.add(1, 2));
        int count = 1;
        for (int i = 0; i < count; i++) {
            String msg = "测试 java -> go,java -> go,java -> go,java -> go,java -> go,java -> go,java -> go,java -> go";
            System.out.println(AuthServer.hello(msg));
        }
        count = 999999999;
}

测试结果

  • 在x86的windows、linux及arm架构的linux均可正常使用
  • 在测试代码中将count数量设得很大时,观察到内存使用在200M左右,很稳定,无明显的内存泄露

参考链接

  • https://liujiacai.net/blog/2020/08/08/go-meet-java/
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值