使用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>

人生不作安期生,醉入东海骑长鲸