GraalVM Native Image C API 完整指南:用 C/C++ 管理 Java 对象与 Isolate 生命周期
GraalVM Native Image C API 完整指南用 C/C 管理 Java 对象与 Isolate 生命周期【免费下载链接】graalGraalVM compiles applications into native executables that start instantly, scale fast, and use fewer compute resources 项目地址: https://gitcode.com/gh_mirrors/gr/graal本指南以 GraalVM 仓库中 C-API.md 文档为核心系统讲解 Native Image 的 C 语言 API如何从 C/C 侧创建 isolate、附加与分离线程、查询线程与 isolate 的对应关系以及安全地拆除 isolate。文中所有头文件声明均可溯源至仓库内的 graal_isolate.preamble 与其对应的 Java 实现 CEntryPointNativeFunctions.java读者阅读后可直接在自己的共享库集成代码中正确使用这套 API。什么是 Native Image C APINative Image 提供了一套 GraalVM 特有的 C 语言 API用于从 C/C 语言中创建和管理 isolateGraalVM 的隔离运行时实例每个 isolate 拥有独立的堆与运行时状态初始化 isolate并将宿主线程**附加attach**到 isolate 上获取当前线程对应的graal_isolatethread_t、反查线程所属的 isolate在线程不再需要时将其分离detach并在合适时机**拆除tear down**整个 isolate。这套 API 的可用前提是Native Image 以共享库shared library形式构建。构建过程中会自动生成对应的头文件所有 C API 声明都包含在该头文件中。也就是说只有当你把包含CEntryPoint入口方法的 Java 类编译为动态链接库.so/.dylib/.dll时生成的graal_isolate.h风格头文件里才会携带这些声明。从源码结构看该头文件由 GraalIsolateHeader.java 驱动生成它以CHeader(value GraalIsolateHeader.class)的形式挂在 CEntryPointNativeFunctions.java 上其writePreamble方法会把 graal_isolate.preamble 中的内容原样写入最终头文件。因此下文给出的结构体定义与 API 原型均可在仓库源码中直接核对。核心数据结构isolate 与 isolate thread不透明句柄类型/* * Structure representing an isolate. A pointer to such a structure can be * passed to an entry point as the execution context. */ struct __graal_isolate_t; typedef struct __graal_isolate_t graal_isolate_t; /* * Structure representing a thread that is attached to an isolate. A pointer to * such a structure can be passed to an entry point as the execution context, * requiring that the calling thread has been attached to that isolate. */ struct __graal_isolatethread_t; typedef struct __graal_isolatethread_t graal_isolatethread_t;graal_isolate_tisolate 的句柄。一个 isolate 对应一个独立的 GraalVM 运行时实例独立的堆、类加载与执行状态。它的指针可以作为入口方法entry point的执行上下文传入。graal_isolatethread_t已附加到某 isolate 的线程句柄。调用入口方法时前提是该调用线程已经附加到了对应 isolate此时把该句柄作为执行上下文传入即可。两者均为不透明结构体opaque structC 侧无需也不应关心其内部布局只需传递与保存指针。辅助类型__graal_uwordpreamble 中还定义了参数结构体使用的无符号字类型#ifdef _WIN64 typedef unsigned long long __graal_uword; #else typedef unsigned long __graal_uword; #endif即 64 位 Windows 上为unsigned long long其余平台为unsigned long宽度均为机器字长用于表达地址空间大小等以字节为单位的量。保护域常量#define NO_PROTECTION_DOMAIN 0 #define NEW_PROTECTION_DOMAIN -1这两个常量对应graal_create_isolate_params_t.pkey字段0表示该 isolate 不属于任何保护域-1表示为它新建一个保护域。该字段属于内部用法普通集成代码保持默认即可。创建 isolate 的参数结构体graal_create_isolate_params_t/* Parameters for the creation of a new isolate. */ enum { __graal_create_isolate_params_version 5 }; struct __graal_create_isolate_params_t { /* Version of this struct. Set to __graal_create_isolate_params_version after zeroing this struct. */ int version; /* Fields introduced in version 1 */ __graal_uword reserved_address_space_size; /* Size of virtual address space to reserve for the heap. */ /* Fields introduced in version 2. Internal usage, do not use. */ const char *auxiliary_image_path; /* Path to an auxiliary image to load. */ __graal_uword auxiliary_image_reserved_space_size; /* Reserved bytes for loading an auxiliary image. */ /* Fields introduced in version 3 */ int argc; /* Number of char* argument strings in argv. */ char **argv; /* Array of argument strings, parsed like command line arguments. */ int pkey; /* Isolate protection key or domain. Internal usage, do not use. */ /* Fields introduced in version 4 */ char ignore_unrecognized_args; /* Ignore unrecognized arguments in argv when 1. */ char _reserved_4; /* Internal usage, do not use. */ /* Fields introduced in version 5 */ char _reserved_5; /* Internal usage, do not use. */ }; typedef struct __graal_create_isolate_params_t graal_create_isolate_params_t;字段说明与使用建议字段引入版本语义使用建议version初始结构体版本号必须置为__graal_create_isolate_params_version当前为 5必填。正确的做法是先把整个结构体清零再写入版本号reserved_address_space_size1为堆预留的虚拟地址空间大小字节可选按需预留较大地址空间可减少动态扩展开销auxiliary_image_path2要加载的辅助镜像auxiliary image路径内部使用不要使用auxiliary_image_reserved_space_size2加载辅助镜像预留的字节数内部使用不要使用argc3argv中char*参数的个数可选类似命令行参数解析argv3参数字符串数组像命令行参数一样被解析可选可用于向 isolate 传递运行参数pkey3isolate 保护键或保护域内部使用不要使用ignore_unrecognized_args4置 1 时忽略argv中无法识别的参数可选传 1 可避免未识别参数导致创建失败_reserved_4/_reserved_54 / 5保留字段保留置 0 即可该结构体具有显式的版本演进机制每一版新增字段都做了标注保持二进制兼容。创建 isolate 时若不需要任何参数可以直接传NULL。典型的初始化写法graal_create_isolate_params_t params; memset(params, 0, sizeof(params)); /* 先清零 */ params.version __graal_create_isolate_params_version; /* 再设版本号 */ params.reserved_address_space_size 0; /* 按需设置 */ params.ignore_unrecognized_args 1; /* 例如容忍未知参数 */完整 C API 函数参考以下六个函数构成 C API 的主体每个函数都有对应的 Java 侧CEntryPoint实现见 CEntryPointNativeFunctions.java二者通过nameTransformation NameTransformation.class将 Java 方法名转换为 C 导出符号名create_isolate、attach_thread等。1. 创建 isolategraal_create_isolateint graal_create_isolate(graal_create_isolate_params_t* params, graal_isolate_t** isolate, graal_isolatethread_t** thread);创建一个新 isolate考虑传入的参数可以为NULL成功返回0失败返回非零值成功后当前线程会自动附加到新创建的 isolate并且 isolate 地址与 isolate thread 地址分别写入传入的指针若指针非NULL。对应 Java 实现CEntryPointNativeFunctions.javaCEntryPoint(name create_isolate, ...) public static int createIsolate(CEntryPointCreateIsolateParameters params, IsolatePointer isolate, IsolateThreadPointer thread) { int result CEntryPointActions.enterCreateIsolate(params); if (result ! 0) { return result; } if (isolate.isNonNull()) { isolate.write(CurrentIsolate.getIsolate()); } if (thread.isNonNull()) { thread.write(CurrentIsolate.getCurrentThread()); } return CEntryPointActions.leave(); }可见其核心调用链是enterCreateIsolate→ 写入 isolate / thread 句柄 →leave与头文件文档注释完全一致。2. 附加线程graal_attach_threadint graal_attach_thread(graal_isolate_t* isolate, graal_isolatethread_t** thread);将当前线程附加到传入的 isolate失败返回非零值成功时把创建的 isolate thread 结构地址写入传入指针并返回0幂等性如果该线程已经附加过调用依然成功并同样返回其 isolate thread 结构。这一点在实现 CEntryPointNativeFunctions.java 中体现为CEntryPointActions.enterAttachThread(isolate, true)。3. 获取当前线程句柄graal_get_current_threadgraal_isolatethread_t* graal_get_current_thread(graal_isolate_t* isolate);给定一个当前线程已附加的 isolate返回该线程对应的 isolate thread 结构地址若当前线程未附加到该 isolate或发生其他错误返回NULL。4. 反查所属 isolategraal_get_isolategraal_isolate_t* graal_get_isolate(graal_isolatethread_t* thread);给定 isolate thread 结构返回其所属 isolate 的结构地址出错时返回NULL。实现上直接读取线程局部数据VMThreads.IsolateTL.get(thread)见 CEntryPointNativeFunctions.java即 isolate 与线程的归属关系保存在线程的 thread-local 数据中。5. 分离线程graal_detach_threadint graal_detach_thread(graal_isolatethread_t* thread);将传入的 isolate thread 从它的 isolate 分离并丢弃与之关联的任何状态或上下文调用时刻该 isolate thread 上下文中不得仍有代码在执行成功返回0失败返回非零值。对应实现先CEntryPointActions.enter(thread)校验上下文再leaveDetachThread()完成分离见 CEntryPointNativeFunctions.java。6. 拆除 isolategraal_tear_down_isolateint graal_tear_down_isolate(graal_isolatethread_t* thread);拆除传入的且仍处于附加状态的isolate thread 所属的整个 isolate会等待所有已附加线程先分离然后丢弃该 isolate 的对象、线程及其他所有关联状态成功返回0失败返回非零值。阻塞排查提示源自头文件文档注释如果此调用无限期阻塞说明仍有 Java 线程在收到Thread.interrupt()事件后没有终止。为避免无限阻塞应在调用此函数前于 Java 侧协调这些线程合作式地关闭。要诊断此类问题可使用选项-R:TearDownWarningSecondssecs检测仍在运行的线程该选项会打印所有阻塞 tear-down 的线程的堆栈轨迹。这组说明在源码中以THREAD_TERMINATION_NOTE常量保存并写入头文件见 CEntryPointNativeFunctions.java与 C-API.md 中的内容一一对应。生命周期使用流程推荐顺序将上述函数组合起来一个典型的 C/C 集成流程如下创建 isolate调用graal_create_isolate(NULL, isolate, thread)首参为NULL表示使用默认参数。创建成功后当前线程即已附加thread可直接用于后续入口调用调用入口方法将thread或isolate作为执行上下文传入共享库导出的 entry point多线程场景其他宿主线程需要访问该 isolate 时先调用graal_attach_thread(isolate, thread)附加可用graal_get_current_thread(isolate)校验是否已附加线程结束不再使用后调用graal_detach_thread(thread)分离整体收尾所有线程分离、Java 侧工作线程已合作式关闭后调用graal_tear_down_isolate(thread)拆除 isolate释放资源。顺序要点graal_tear_down_isolate会等待附加线程先分离因此步骤 4 与 5 的顺序至关重要若 Java 侧启动了非守护性质的长期运行线程务必在拆除前让其响应中断退出否则拆除调用可能无限阻塞可借助-R:TearDownWarningSecondssecs诊断。与 JNI Invocation API 的关系在 C 级 API 之外还可以使用 JNI Invocation API 从 Java 侧创建 isolate并暴露、调用内嵌在 native 共享库中的 Java 方法。二者定位不同Native Image C API面向从 C/C 直接驱动 isolate 的生命周期管理偏底层、面向宿主语言直接集成JNI Invocation API走 JNI 标准通道便于在既有 JNI 生态中复用从 Java 侧发起创建与调用。实际项目中可根据宿主代码的技术栈与已有依赖选择合适的通道二者可互为补充。从仓库获得的进一步学习资源围绕 C API 主题仓库中还提供了以下可直接研读的材料头文件生成机制GraalIsolateHeader.java 通过CHeader声明与writePreamble将 graal_isolate.preamble 写入生成的头文件全部入口函数实现CEntryPointNativeFunctions.java 除上述六个函数外还包含detach_all_threads_and_tear_down_isolate分离所有外部启动的附加线程后整体拆除适用于简化收尾流程的场合完整的 C 集成示例cinterfacetutorial.c 展示了 C 函数指针回调 Java、结构体/联合体数据传递等完整用法配套官方指南构建 Native 共享库指南讲解如何把 Java 类构建为共享库并生成 C 头文件与 Native 代码互操作更广泛的 native 互操作主题JNI Invocation APIJava 侧创建 isolate 的替代通道。常见问题速查为什么我的头文件里没有这些声明因为 C API 只在 Native Image以共享库形式构建时生成且生成内容由CHeader(GraalIsolateHeader.class)关联的入口类驱动。请确认构建目标为 shared library。params可以直接传NULL吗可以。graal_create_isolate的文档明确说明参数可为NULL此时使用默认配置创建 isolate。线程重复 attach 会怎样不会出错。graal_attach_thread对已附加线程幂等会直接返回已有的 isolate thread 结构。graal_tear_down_isolate卡住怎么办按头文件建议在 Java 侧协调线程响应中断并退出再用-R:TearDownWarningSecondssecs定位仍在运行的线程及其堆栈。_reserved_4/_reserved_5/auxiliary_image_*/pkey字段怎么用头文件明确标注为“Internal usage, do not use”普通集成请将其保持为零值或默认值仅依赖已公开语义的字段。【免费下载链接】graalGraalVM compiles applications into native executables that start instantly, scale fast, and use fewer compute resources 项目地址: https://gitcode.com/gh_mirrors/gr/graal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考