在 Java 中调用 Go 的大致过程如下go --> cgo --> jna --> java

整个过程要解决的问题主要两个:数据类型在两种语言中如何转化

何时清理无用的数据

下面就围绕上述调用过程来阐述,本文涉及代码完整版可以下面链接找到:

Go -> Cgo

这是跨语言调用的第一步,主要是借助 cgo,把 Go 代码编译 C 共享库。

cgo 是 Go 语言提供与 C 语言互调的一工具。提供一个名为 C 的伪 package,供 Go 访问 C 中的变量与函数,如 C.size_t C.stdout 等;同时提供 5 个特殊函数,用于两种语言间类型的转化:// Go string to C string

// The C string is allocated in the C heap using malloc.

// It is the caller's responsibility to arrange for it to be

// freed, such as by calling C.free (be sure to include stdlib.h

// if C.free is needed).

func C.CString(string) *C.char

// Go []byte slice to C array

// The C array is allocated in the C heap using malloc.

// It is the caller's responsibility to arrange for it to be

// freed, such as by calling C.free (be sure to include stdlib.h

// if C.free is needed).

func C.CBytes([]byte) unsafe.Pointer

// C string to Go string

func C.GoString(*C.char) string

// C data with explicit length to Go string

func C.GoStringN(*C.char, C.int) string

// C data with explicit length to Go []byte

func C.GoBytes(unsafe.Pointer, C.int) []byte

需要注意一点,cgo 中函数不能直接返回 slice/map 等具有 go pointer (区别与 C pointer,由 go runtime 管理生命周期)的数据类型,否则会报下面的 panic 信息:panic: runtime error: cgo result has Go pointer

原因也很简单,go 是有 gc 的,假如允许返回具有 go pointer 的数据,那么 C 代码中得到的数据无法保证合法性,很有可能已经被 gc 了,即悬挂指针问题。解决的方式也很简单,就是采用 go 提供的特殊转化函数,将数据转为 unsafe.Pointer,在 C 中用 void * 的方式去使用。

可以想象,这些特殊转化函数一定对数据进行了深拷贝,来保证数据的合法性,可参考 C.CBytes 的定义const cBytesDef = `

func _Cfunc_CBytes(b []byte) unsafe.Pointer {

p := _cgo_cmalloc(uint64(len(b)))

pp := (*[1<<30]byte)(p)

copy(pp[:], b)

return p

}

`

但这也意味着,Go/C 代码中需要负责 free 掉无用的数据(至于哪边 free,要看实际情况)。示例:func main() {

cs := C.CString("Hello from stdio")

C.myprint(cs)

C.free(unsafe.Pointer(cs))

}

将 Go 函数导出供 C 调用,需要用 //export 标示相关函数,并且 Go 文件需要在 package main下。然后用类似下面的 build 命令,即可得到与 C 互调的动态库,同时会生产一个头文件,里面有 export 函数的相关签名。# linux 下可输出到 libawesome.so,这里以 Mac 下的动态库为例

go build -v -o libawesome.dylib -buildmode=c-shared ./main.go//export Hello

func Hello(msg string) *C.char {

return C.CString("hello " + strings.ToUpper(msg))

}

// 头文件中 Hello 的定义

// ptrdiff_t is the signed integer type of the result of subtracting two pointers.

// n 这里表示字符串的长度

typedef struct { const char *p; ptrdiff_t n; } _GoString_;

extern char* Hello(GoString p0);

Cgo -> JNA

这一步主要是 Java 中如何调用 C 代码,目前主要有两种方式,JNA,优势是调用方便,只需要编写 Java 代码,JNA 框架负责在 C/Java 中进行数据类型转化

JNI,优势是性能好,缺点是调用繁琐

详细区别这里不展开叙述,感兴趣的读者可参考下面文章:

JNA -> Java

这一步主要是在 Java 代码中如何调用 JNA 框架提供的库进行跨语言调用,也是本文的重点。

JNA 将 Java 基本类型直接映射为 C 中同等大小的类型,这里摘抄如下Native TypeSizeJava TypeCommon Windows Typeschar8-bit integerbyteBYTE, TCHAR

short16-bit integershortWORD

wchar_t16/32-bit charactercharTCHAR

int32-bit integerintDWORD

intboolean valuebooleanBOOL

long32/64-bit integerNativeLongLONG

long long64-bit integerlong__int64

float32-bit FPfloat

double64-bit FPdouble

char*C stringStringLPCSTR

void*pointerPointerLPVOID, HANDLE, LPXXX

对于 C 中的 struct/pointer,JNA 中也提供了 Structure/Pointer 类来对应。JNA 的具体使用过程可参考:

上述 GettingStarted 中第三种加载动态库的方式(即 resources 下的 {OS}-{ARCH}/{LIBRARY} 目录内)可以把动态库一起打包到 jar 中,这对于提供基础类库时比较方便,用户不需要再额外配置。resources/

├── darwin

│   └── libawesome.dylib

├── linux-x86-64

│   └── libawesome.so

vladimirvivien/go-cshared-examples 这个仓库演示了四个函数 Add/Cosine/Sort/Log 的 JNA 调用,但这四个函数的返回类型都是基本类型(int/float64),没有 string/slice 等复杂类型,因此这里通过五个示例讲述复杂类型的返回问题:BadStringDemo.java 本示例演示了网络上一种常见,但有内存泄露问题的返回 string 的方式

GoodStringDemo.java 这个示例演示了如何正确的返回 string

ReturnByteSliceDemo.java 本示例演示如何返回 slice,以及如何在 Java 中处理 Go 中的多个返回值

ReturnInterfaceDemo.java 本示例演示返回具有 Go Pointer 的结构时的报错行为

上述示例均使用 direct mapping 的方式做 JNA,这种方式性能更好,但是支持的参数类型有限,读者可参考 vladimirvivien/go-cshared-examples 学习 interface mapping 的使用方式。

总结

C 语言作为连接不同高级语言的胶水语言,不具备垃圾回收功能,所以开发者在做 JNA 时要注意回收无用的内存结构。

参考

java 调用 go_实践总结:在 Java 中调用 Go 代码相关推荐

  1. java批量处理数据库语句_Java项目中调用bat批处理进行多用户数据库备份

    Java项目中调用bat批处理配合使用BCP进行多用户数据的备份 一.项目需求 最近项目中需要对数据库(Sql Server系列数据库)进行备份.项目中的需求不是简单的整个数据库的备份,而是根据用户来 ...

  2. Python利用JPype调用Java对象方法【实现在Python中调用JAVA】

    一.JPype简述 1.JPype是什么? JPype是一个能够让 python 代码方便地调用 Java 代码的工具,从而克服了 python 在某些领域(如服务器端编程)中的不足. 2.JPype ...

  3. java如何调c 的接口_JAVA中调用C语言接口

    写一个Java类,在这个类中包含了需要调用的本地方法的描述. //WinMsgBox.java package edu.netcom.jni; public class WinMsgBox  { st ...

  4. java调用多条cmd命令_Java中调用多条cmd命令

    使用Java执行cmd代码很简单: try{ Runtime.getRuntime().exec("cd /d E:"); }catch (Exception e) { e.pri ...

  5. java for 最后_Java的for循环中调用了查询服务,最后只打印了循环的最后一条数据(循环次数)次...

    问题描述 1.首先我查询了出来一个对象集合(包含1和2俩个对象) 2.利用上面集合中的某数据循环查询下一个表中的数据.(也就是for循环中调用了查询方法,返回一个对象) 3.在循环体中我需要用到第一次 ...

  6. java 异常 最佳实践_关于JAVA异常处理的20个最佳实践

    在我们深入了解异常处理最佳实践的深层概念之前,让我们从一个最重要的概念开始,那就是理解在JAVA中有三种一般类型的可抛类: 检查性异常(checked exceptions).非检查性异常(unche ...

  7. java 异常 最佳实践_处理Java异常的10种最佳实践

    java 异常 最佳实践 在本文中,我们将看到处理Java异常的最佳实践. 用Java处理异常不是一件容易的事,因为新手很难理解,甚至专业的开发人员也可能浪费时间讨论应该抛出或处理哪些Java异常. ...

  8. java restful接口开发实例_Spring Boot 中 10 行代码构建 RESTful 风格应用!

    点击上方"Java后端技术",选择"置顶或者星标" 你关注的就是我关心的! 作者:江南一点雨 微信公众号:牧码小子(ID:a_javaboy) 推荐阅读:10个 ...

  9. java 微服务实践 视频,全新JAVA微服为务实战Spring Boot系列视频教程 小马哥 JAVA微服务实践视频课程...

    全新JAVA微服为务实战Spring Boot系列视频教程 小马哥 JAVA微服务实践视频课程 ===============课程目录=============== ├─(1) 03Java 微服务实 ...

最新文章

  1. php中的全局变量$_POST收集表单数据
  2. 云计算的台前幕后 ——微云网络
  3. 一文全解析——APP版本管理基本知识
  4. 【译】From Smart Contracts to Courts with not so Smart Judges
  5. 学习 WCF (5)--学习Binding的使用
  6. Wyn Enterprise 核心功能:系统集成
  7. 自己动手写spring(三) 支持注解方式
  8. java 实例域_Java实例域初始化方法及顺序
  9. AudioContextOptions
  10. Windows Azure真实案例--Lokad 公司通过软件+服务高效提供先进的预测服务
  11. 文字的多列布局--column
  12. [老老实实学WCF] 第五篇 再探通信--ClientBase
  13. html中支持的常用中文字体
  14. 14. 接口隔离模式之Facade模式(门面模式/外观模式)
  15. 数字后端设计相关文件说明
  16. 汉诺塔(C语言实现)
  17. 大咖|英特尔中国研究院院长宋继强:我们是如何与李宇春打造全球第一支三维人脸特效的音乐视频的
  18. 各个保险中个人与企业所缴费的比例
  19. 网络编程01-TCP协议(详)
  20. vscode remote ssh无法建立连接或一直需要输入密码

热门文章

  1. (JAVA)超大整数运算
  2. android webview 图表,Android WebView 无法正常显示网页图表
  3. 【C语言进阶深度学习记录】十八 条件编译的使用与分析
  4. 【Git、GitHub、GitLab】二 Git基本命令之建立Git仓库
  5. 【02】Kubernets:使用 kubeadm 部署 K8S 集群
  6. java虚拟机10.内存模型与线程
  7. swift UIActivityIndicatorView
  8. 计算机工程与应用单像素成像,2011计算机工程与应用基于压缩感知理论的单像素成像系统研究_白凌云.pdf...
  9. sql如何遍历几百万的表_SQL Server遍历表中记录的2种方法(使用表变量和游标)
  10. 软件测试之逻辑覆盖测试理论总结(白话文)