The general procedure for calling Go in Java is as follows
|
|
There are two main problems to be solved in the whole process.
- how to transform data types in both languages
- when to clean up the useless data
The following is the process around the above call to elaborate, this article involves the full version of the code can be found in the following link:.
Go -> Cgo
This is the first step in cross-language calling, mainly with the help of cgo, which compiles Go code into C shared libraries. cgo is a tool for Go to provide interoperability with C. It provides a pseudo package called C
for Go to access variables and functions in C, such as C.size_t
, C.stdout
, etc. It also provides 5 special functions for type conversion between the two languages.
|
|
Note that functions in cgo cannot directly return slice/map data types with a go pointer (as opposed to a C pointer, which is managed by the go runtime), otherwise the following panic message will be reported.
|
|
The reason is also very simple, go has gc, if allowed to return data with go pointer, then the data obtained in C code can not guarantee the legality, it is likely to have been gc, that is, the hanging pointer problem. The solution is simple: use the special conversion functions provided by go to convert the data to unsafe.Pointer
and use it in C as void *
.
As you can imagine, these special conversion functions must make a deep copy of the data to ensure its legality, see C.CBytes definition
But it also means that the Go/C code is responsible for freeing the useless data (which side does the freeing depends on the actual situation). Example.
To export Go functions for C, you need to mark the functions with //export
and the Go file needs to be under package main
. Then you can use a build command like the one below to get a dynamic library that interoperates with C and produces a header file with the relevant signatures of the export functions.
|
|
The complete code can be found in main.go, the corresponding header file libawesome.h.
Cgo -> JNA
This step is mainly about how to call C code in Java, there are two main ways, the
- JNA, the advantage is easy to call, only need to write Java code, JNA framework is responsible for data type conversion in C/Java
- JNI, the advantage is good performance, the disadvantage is cumbersome to call
Without going into the details of the differences, interested readers can refer to the following article.
JNA -> Java
This step focuses on how to call the libraries provided by the JNA framework for cross-language calls in Java code, and is the focus of this article. JNA maps Java basic types directly to C types of the same size, here excerpt is as follows
Native Type | Size | Java Type | Common Windows Types |
---|---|---|---|
char | 8-bit integer | byte | BYTE, TCHAR |
short | 16-bit integer | short | WORD |
wchar_t | 16/32-bit character | char | TCHAR |
int | 32-bit integer | int | DWORD |
int | boolean value | boolean | BOOL |
long | 32/64-bit integer | NativeLong | LONG |
long long | 64-bit integer | long | __int64 |
float | 32-bit FP | float | |
double | 64-bit FP | double | |
char* | C string | String | LPCSTR |
void* | pointer | Pointer | LPVOID, HANDLE, LPXXX |
For struct/pointer in C, JNA also provides Structure/Pointer class to correspond to it. JNA’s specific usage procedure can be found at
- https://github.com/java-native-access/jna/blob/master/www/GettingStarted.md
- http://java-native-access.github.io/jna/5.6.0/javadoc/overview-summary.html
The third way of loading dynamic libraries in GettingStarted above (i.e. in the {OS}-{ARCH}/{LIBRARY}
directory under resources) can package dynamic libraries into jars together, which is convenient for providing base libraries without additional configuration by the user.
vladimirvivien/go-cshared-examples This repository demonstrates JNA calls to four functions Add/Cosine/Sort/Log, but the return types of all four functions are basic types (int/float64) and there are no complex types such as string/slice, so here are five examples of complex type return problems.
- BadStringDemo.java This example demonstrates a common, but memory-leaking, way of returning a string on the web.
- GoodStringDemo.java This example demonstrates how to properly return the string
- AutoClosableStringDemo.java This example uses the AutoCloseable and try-with-resource features to free memory on top of GoodStringDemo
- ReturnByteSliceDemo.java This example demonstrates how to return a slice and how to handle multiple return values in Go in Java
- ReturnInterfaceDemo.java This example demonstrates the exception behavior when returning a structure with a Go Pointer
The above examples all use direct mapping to do JNA, readers can refer to vladimirvivien/go-cshared-examples to learn how to use interface mapping. Here is a simple performance test of the two mapping methods, and the stress test data is as follows.
Method | input | output | which is better | rate |
---|---|---|---|---|
Add | two primitive ints | int | direct-mapping | 1.38 |
Hello | string | FreeableString | interface-mapping | 1.169 |
Hello2 | string | Pointer | direct-mapping | 1.0083 |
The conclusion is
direct-mapping is better for basic types (including Pointer) and interface-mapping is slightly better for complex types.
The reason can be found at: https://stackoverflow.com/a/38081251
Summary
C, as a glue language to connect different high-level languages, does not have garbage collection, so developers should pay attention to recycling useless memory structures when doing JNA.