使用graalvm将java编译成动态链接库供其他语言调用
1 java代码
MyLibrary.java
public class MyLibrary {
public static void helloWorld() {
System.out.println("Hello, World from GraalVM!");
}
public static int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
if (args.length == 2) {
try {
System.out.println("Start running the java method!");
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
System.out.println("Add(" + args[0] + ", " + args[1] + ") = " + add(a, b));
return;
} catch (NumberFormatException e) {
}
}
helloWorld();
}
}
使用javac编译成class文件 MyLibrary.class
2 使用graalvm的native-image工具编译成动态链接库
D:\develop\jdk\bellsoft-liberica-vm-full-openjdk17-23.0.7\bin\native-image --shared -H:Name=libmylib MyLibrary
输出如下
========================================================================================================================
GraalVM Native Image: Generating 'libmylib' (shared library)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing... (7.6s @ 0.13GB)
Java version: 17.0.14+10-LTS, vendor version: Liberica-NIK-23.0.7-1
Graal compiler: optimization level: 2, target machine: x86-64-v3
C compiler: cl.exe (microsoft, x64, 19.42.34435)
Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis... [****] (21.1s @ 0.51GB)
2,902 (71.62%) of 4,052 types reachable
3,527 (50.81%) of 6,941 fields reachable
13,254 (44.22%) of 29,976 methods reachable
899 types, 0 fields, and 350 methods registered for reflection
62 types, 53 fields, and 52 methods registered for JNI access
1 native library: version
[3/8] Building universe... (3.3s @ 0.59GB)
[4/8] Parsing methods... [**] (2.2s @ 0.33GB)
[5/8] Inlining methods... [***] (1.5s @ 0.71GB)
[6/8] Compiling methods... [****] (18.1s @ 0.63GB)
[7/8] Layouting methods... [*] (1.5s @ 0.82GB)
[8/8] Creating image... [**] (4.8s @ 0.96GB)
4.59MB (38.92%) for code area: 7,555 compilation units
7.03MB (59.68%) for image heap: 90,517 objects and 5 resources
168.19kB ( 1.39%) for other data
11.78MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area: Top 10 object types in image heap:
3.45MB java.base 1011.20kB byte[] for code metadata
870.52kB svm.jar (Native Image) 897.34kB java.lang.String
113.73kB java.logging 894.33kB byte[] for general heap data
62.75kB org.graalvm.nativeimage.base 674.76kB byte[] for java.lang.String
24.29kB jdk.internal.vm.ci 670.91kB java.lang.Class
24.11kB org.graalvm.sdk 356.78kB char[]
6.19kB jdk.internal.vm.compiler 318.66kB java.util.HashMap$Node
1.35kB jdk.proxy1 249.39kB com.oracle.svm.core.hub.DynamicHubCompanion
1.30kB MyLibrary 189.02kB java.lang.String[]
1.27kB jdk.proxy3 172.55kB java.lang.Object[]
1.57kB for 2 more packages 1.25MB for 824 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
HEAP: Set max heap for improved and more predictable memory usage.
CPU: Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
2.0s (3.3% of total time) in 36 GCs | Peak RSS: 1.48GB | CPU load: 5.45
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
D:\graalvm\demo\graal_isolate.h (c_header)
D:\graalvm\demo\graal_isolate_dynamic.h (c_header)
D:\graalvm\demo\libmylib.dll (shared_library)
D:\graalvm\demo\libmylib.h (c_header)
D:\graalvm\demo\libmylib.lib (import_library)
D:\graalvm\demo\libmylib_dynamic.h (c_header)
========================================================================================================================
Finished generating 'libmylib' in 1m 1s.
完成后结果如下
┌──(冰白寒祭㉿冰白寒祭)-[D:/graalvm/demo] 0ms ✓
└─$ tree
.
├── MyLibrary.class
├── MyLibrary.java
├── graal_isolate.h
├── graal_isolate_dynamic.h
├── libmylib.dll
├── libmylib.h
├── libmylib.lib
└── libmylib_dynamic.h
1 directory, 8 files
其中
libmylib.h
#ifndef __LIBMYLIB_H
#define __LIBMYLIB_H
#include <graal_isolate.h>
#if defined(__cplusplus)
extern "C" {
#endif
int run_main(int argc, char** argv);
#if defined(__cplusplus)
}
#endif
#endif
libmylib_dynamic.h
#ifndef __LIBMYLIB_H
#define __LIBMYLIB_H
#include <graal_isolate_dynamic.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef int (*run_main_fn_t)(int argc, char** argv);
#if defined(__cplusplus)
}
#endif
#endif
3 使用python来调用dll
myprogram.py
import ctypes
import os
import sys
# 加载DLL
dll_path = os.path.abspath("libmylib.dll")
mylib = ctypes.CDLL(dll_path)
# 定义函数原型
mylib.run_main.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
mylib.run_main.restype = ctypes.c_int
# 从命令行获取参数
# sys.argv[0] 是脚本名称,因此我们从 sys.argv[1:] 获取实际参数
script_args = sys.argv[1:] # 例如: ["2", "3"]
# 准备参数
# 第一个参数是程序名称(模拟 Java 的 main 方法)
args = ["MyLibrary".encode('utf-8')] + [arg.encode('utf-8') for arg in script_args] + [None]
argc = len(args) - 1
argv = (ctypes.c_char_p * len(args))(*args)
# 调用DLL中的函数
result = mylib.run_main(argc, argv)
print(f"Result: {result}")
执行
┌──(冰白寒祭㉿冰白寒祭)-[D:/graalvm/demo] 0ms ✓
└─$ python myprogram.py 2 9
Start running the java method!
Add(2, 9) = 11
Result: 0
┌──(冰白寒祭㉿冰白寒祭)-[D:/graalvm/demo] 40ms ✓
└─$ python myprogram.py 0 5
Start running the java method!
Add(0, 5) = 5
Result: 0
┌──(冰白寒祭㉿冰白寒祭)-[D:/graalvm/demo] 39ms ✓
└─$ python myprogram.py
Hello, World from GraalVM!
Result: 0
4 使用c语言来调用
myprogram.c
#include <stdio.h>
#include <windows.h>
// 定义函数指针类型
typedef int (*run_main_t)(int, char**);
int main(int argc, char* argv[]) {
// 检查命令行参数
if (argc < 2) {
printf("Usage: %s <arg1> <arg2> ...\n", argv[0]);
return 1;
}
// 加载DLL
HMODULE hDll = LoadLibrary("libmylib.dll");
if (hDll == NULL) {
printf("Failed to load DLL: error code %lu\n", GetLastError());
return 1;
}
// 获取函数指针
run_main_t run_main = (run_main_t)GetProcAddress(hDll, "run_main");
if (run_main == NULL) {
printf("Failed to get function address: error code %lu\n", GetLastError());
FreeLibrary(hDll);
return 1;
}
// 准备参数
// 第一个参数是程序名称(模拟 Java 的 main 方法)
char** args = (char**)malloc((argc + 1) * sizeof(char*));
if (args == NULL) {
printf("Failed to allocate memory for arguments\n");
FreeLibrary(hDll);
return 1;
}
args[0] = "MyLibrary"; // 模拟 Java 的 main 方法参数
for (int i = 1; i < argc; i++) {
args[i] = argv[i]; // 从命令行参数中复制
}
args[argc] = NULL; // 参数列表以 NULL 结尾
// 调用DLL中的函数
int result = run_main(argc, args);
// 释放内存
free(args);
// 释放DLL
FreeLibrary(hDll);
printf("Result: %d\n", result);
return 0;
}
编译运行
D:\graalvm\demo>cl myprogram.c libmylib.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34435 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
myprogram.c
Microsoft (R) Incremental Linker Version 14.42.34435.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:myprogram.exe
myprogram.obj
libmylib.lib
D:\graalvm\demo>myprogram.exe 2 3
Start running the java method!
Add(2, 3) = 5
Result: 0
D:\graalvm\demo>myprogram.exe
Hello, World from GraalVM!
Result: 0
D:\graalvm\demo>