Graalvm配置文件与Feature和Substitute机制介绍
GraalVM介绍
GraalVM提前将Java应用程序编译成独立与机器码二进制文件(可执行文件、动态库文件),如windows系统中的exe文件和dll文件。与在Java虚拟机(JVM)上运行的应用程序相比,这些二进制文件更小,启动速度快100倍,在没有预热的情况下提供峰值性能,并且使用更少的内存和CPU。
下面只介绍开发中与Graalvm相关的配置文件和扩展点(Feature和Substitute机制)以及maven插件配置。
1. 配置文件
1.1 动态代码配置文件
对于动态代码,需要通过指定的json文件来描述其metadata,这样GraalVM才能正确的编译和优化代码。如下类型
- 反射,配置文件名:reflect-config.json
- 动态代理, 配置文件名:proxy-config.json
- 资源文件, 配置文件名:resource-config.json
- JNI, 配置文件名:jni-config.json,配置被JNI代码调用的java方法,可见jni-lib模块
- 资源序列化, 配置文件名:serialization-config.json
- predefined classes, 配置文件名:predefined-classes-config.json
这些JSON文件内容格式可见github: graalvm json file schema
1.2 命令参数配置文件
graalvm推荐开发者为自己的项目提供文件名为native-image.properties的配置文件,在该配置文件中可指定native-image编译分析所需要的配置,在properties文件中,支持如下key:
- Args 配置native-image命令参数,如–feature指定扩展的Feature接口、–initialize-at-build-time指定编译时初始化的类。更多命令行选项见Command-line Options
- JavaArgs 配置native-image执行时的JVM参数
- ImageName 配置最后生成的可执行文件名称
关于native-image.properties的内容更多具体的可见文档Native Image Build Configuration
1.3 存放位置
配置文件都存放在项目jar文件的META-INF/native-image/目录下,native-image会自动加载该目录下的文件。如:
META-INF/
└── native-image└── native-image.properties└── reflect-config.json└── jni-config.json
为了防止多个jar文件内的配置冲突,graalvm推荐在META-INF/native-image/目录下新建以你项目的groupId/artifactID为子目录,如:
META-INF/
└── native-image└── groupID└── artifactID└── native-image.properties└── reflect-config.json└── jni-config.json
2. 动态代码配置示例
2.1 反射
- 类所有方法都可以用于反射,reflect-config.json配置如下
[{"name": "com.example.User","allDeclaredConstructors": true,"allPublicMethods": true,"allDeclaredFields": true}
]
- 类中指定方法和字段可用于反射,配置示例如下
[{"name": "com.example.User","fields": [{"name": "name"}],"methods": [{"name": "<init>","parameterTypes": []},{"name": "setName","parameterTypes": ["java.lang.String"]}]}
]
2.2 动态代理
- 假设接口
com.example.IPrintServic会用于动态代理,proxy-config.json配置示例如下
[{"interfaces": ["com.example.IPrintService"]}
]
2.3 资源文件
假设有如下代码用于加载类路径下的资源
try(InputStream stream = this.getClass().getClassLoader().getResourceAsStream(“resource_config.properties”)) {//读取资源内容
}
resource-config.json配置示例如下
{"resources": {"includes": [{"pattern": "\\Qresource_config.properties\\E"}]}
}
说明: 上面pattern属性值为正则表达式,字符\Q和\E之间的内容在正则表达式中表示字面量,即对其中的特殊字符进行原始匹配,比如上面的.不是匹配任意字符,而就是匹配字符串中的.。
2.4 类序列化
如果项目中类com.example.User使用了JDK的序列化方式,则其serialization-config.json配置示例如下
[{"name": "com.example.User"}
]
2.5 JNI中调JAVA API
如果JNI代码中有调com.example.Utils类的public static int add(int,int)方法,则jni-config.json配置示例如下
[
{"name":"com.example.Utils","methods":[{"name":"add","parameterTypes":["int","int"] }]
}
]
3. 扩展Feature接口
native-image生成可执行代码过程中会在关键步骤执行用户自定义代码,而用户代码的执行是通过graalvm的Feature机制实现的,开发者可以实现Feature接口来为程序设置一些特定行为,比如通过编码的方式设置以上动态代码配置。
示例
示例需求: 为com.example包下注解了@Reflect注解的类注册反射配置
- 实现Feature接口
public class ReflectFeature implements Feature {@Overridepublic void beforeAnalysis(BeforeAnalysisAccess access) {// 注册元数据try (ScanResult graph = new ClassGraph().overrideClassLoaders(access.getApplicationClassLoader()).overrideClasspath(access.getApplicationClassPath()).enableAllInfo().acceptPackages("com.example").scan()) {graph.getClassesWithAnnotation(Reflect.class).forEach(classInfo -> {Class clazz = classInfo.loadClass();RuntimeReflection.register(clazz);RuntimeReflection.registerForReflectiveInstantiation(clazz); // 可通过newInstance()方法创建,与allDeclaredConstructors=true类似RuntimeReflection.register(clazz.getDeclaredFields()); // allDeclaredFields=true类似RuntimeReflection.register(clazz.getDeclaredMethods());// allPublicMethods=true类似});}}
}
注:以上包扫描使用classgraph工具,它与其它反射工具相比优势在于它是直接解析字节码来进行匹配的,好处就是不用把类加载到JVM中。classgraph的maven配置如下:
<dependency><groupId>io.github.classgraph</groupId><artifactId>classgraph</artifactId><version>4.8.173</version>
</dependency>
- 注册自定义的
com.example.feature.ReflectFeature接口
有两种方式,- 方式一:在native-image命令行使用参数配置
--features=com.example.feature.ReflectFeature指定 - 方式二:推荐在native-image.properties文件配置,内容如下
Args = --features=com.st.graalvm.feature.StepTrackFeature - 方式一:在native-image命令行使用参数配置
4. Substitute机制
Substitute替换机制使得可以在不修改源代码的前提下,对运行时行为进行适配改造,以保持对原API的兼容。 GraalVM本身也通过该机制对JDK API做了很多兼容性替换。
基于该机制,开发者也可以根据实际需求对一些类、方法、字段甚至是构造函数在native-image编译过程中进行替换。
替换通过注解来实现,native-image编译过程会扫描这些特定注解的类,替换类必须为final类型,命名格式推荐为Target_{原类包名}_{原类名}。
替换机制提供的注解如下
-
@TargetClass 注解替换类,其value值为被替换类
-
@Substitute 注解替换方法,被注解的方法在方法名和签名上需与目标方法一致
-
@Alias 注解在替换字段上,被注解的字段在名称和签名上需与目标字段一致,
- 搭配**@InjectAccessors**注解可拦截字段的get和set方法,示例如下
@Alias @InjectAccessors(Target_OriginClass_Version_value.class) private static String version; static class Target_OriginClass_Version_value {private static String versionValue;static String getVersion() {if (versionValue == null) {versionValue = "44444444L_substitute";}return versionValue;}static void setVersion(String value) {System.out.println("setVersion:" + value);} }- 搭配**@RecomputeFieldValue**注解可替换字段的值,示例如下
// 替换静态字段,需设置isFinal=true @Alias @RecomputeFieldValue(kind = Kind.FromAlias, isFinal = true) private static boolean useNative = true; // 替换字段 @Alias @RecomputeFieldValue(kind = Kind.FromAlias) private boolean inited = true; -
@Inject 注解在替换类的字段上,该字段在被替换类中不存在,需搭配
@RecomputeFieldValue注解,示例如下
@TargetClass(OriginClass.class)
public final class Target_com_example_OriginClass {// 替换并设置字段值@Alias @InjectAccessors(Target_OriginClass_Type_value.class)private TypeEnum type;@Inject @RecomputeFieldValue(kind = Kind.Reset)private TypeEnum typeValue;static class Target_OriginClass_Type_value {static TypeEnum getType(Target_OriginClass receiver) {if (receiver.typeValue == null) {receiver.typeValue = TypeEnum.SUBSTITUTE;}return receiver.typeValue;}static void setType(Target_OriginClass receiver, TypeEnum value) {receiver.typeValue = value;}}
}
5. maven编译打包配置
<plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>0.10.2</version><extensions>true</extensions><executions><execution><id>build-native</id><goals><goal>compile-no-fork</goal></goals><phase>package</phase></execution><execution><id>test-native</id><goals><goal>test</goal></goals><phase>test</phase></execution></executions><configuration><imageName>${project.artifactId}</imageName><sharedLibrary>false</sharedLibrary><metadataRepository><enabled>true</enabled></metadataRepository></configuration>
</plugin>
6. 其它特性
- 创建动态库供C/C++,可见文档Build a Native Shared Library
- JAVA代码可以直接调动态库接口,不过java代码需要经过native-image编译后才能运行
- 创建JNI接口,在之前JNI接口实现都是C/C++编写,使用native-image可以直接使用JAVA来编写JNI接口实现,可见文档JNI Invocation API
- 与其它语言集成(包含的语言有JS/Node.js、Python、R、Ruby、WebAssembly、LLVM语言),在Truffle基础上实现,可见文档Embedding Languages
工具
1. 使用native-image-agent代理库生成metadata文件
如果不清楚应用中有哪些动态代码需要提供配置,可以使用graalvm提供native-image-agent来跟踪代码执行情况来生成metadata文件,只需要在java启动参数中加上-agentlib:native-image-agent参数即可,命令示例如下
java -agentlib:native-image-agent=config-output-dir=graalvm -jar graalvm-1.0-SNAPSHOT.jar
总结
- Java语言的动态特性阻碍了native-image静态分析和编译,开发者需要提供相应的配置文件native-image才能完整的识别出需要静态编译的代码。
- Feature机制和替换机制为开发者提供了在编译期间尽可能多的控制
- 通过native-image不仅可以把应用编译为可执行文件,也可以把公共库编译为动态库供其它语言调用,还可以把公共库通过JNI接口暴露给其它java程序使用。
相关文章:
Graalvm配置文件与Feature和Substitute机制介绍
GraalVM介绍 GraalVM提前将Java应用程序编译成独立与机器码二进制文件(可执行文件、动态库文件),如windows系统中的exe文件和dll文件。与在Java虚拟机(JVM)上运行的应用程序相比,这些二进制文件更小,启动速…...
Appium adb 获取appActivity
方法一(最简单有效的方法) 通过cmd命令,前提是先打开手机中你要获取包名的APP adb devices -l 获取连接设备详细信息 adb shell dumpsys activity | grep mFocusedActivity 有时获取到的不是真实的Activity 方法二 adb shell monkey -p …...
调整分区失败致盘无法访问:深度解析与数据恢复全攻略
调整分区失败盘打不开的困境 在计算机的日常维护与管理中,调整磁盘分区是常见的操作之一,旨在优化存储空间布局、提升系统性能或满足特定应用需求。然而,当这一操作未能如预期般顺利进行,反而导致分区调整失败,进而使…...
试用笔记之-汇通计算机等级考试软件一级Windows
首先下载汇通计算机等级考试软件一级Windows http://www.htsoft.com.cn/download/htwork.rar...
Java的NIO体系
目录 NIO1、操作系统级别下的IO模型有哪些?2、Java语言下的IO模型有哪些?3、Java的NIO应用场景?相比于IO的优势在哪?4、Java的IO、NIO、AIO 操作文件读写5、NIO的核心类 :Buffer(缓冲区)、Channelÿ…...
自下而上的选股与自上而下的选股
一起学习了《战胜华尔街》,不知道大家有没有这么一种感受:林奇的选股方法是典型的自下而上的选股方法。虽然这一点没有单独拎出来讨论过,但在《从低迷中寻找卓越》《如何通过财务指标筛选股票?》《边逛街边选股?》《好…...
Tech Talk:智能电视eMMC存储的五问五答
智能电视作为搭载操作系统的综合影音载体,以稳步扩大的市场规模走入越来越多的家庭,成为人们生活娱乐的重要组成部分。存储部件是智能电视不可或缺的组成部分,用于保存操作系统、应用程序、多媒体文件和用户数据等信息。智能电视使用eMMC作为…...
scikit-learn教程
scikit-learn(通常简称为sklearn)是Python中最受欢迎的机器学习库之一,它提供了各种监督和非监督学习算法的实现。下面是一个基本的教程,涵盖如何使用sklearn进行数据预处理、模型训练和评估。 1. 安装和导入包 首先确保安装了…...
CentOS 7 搭建rsyslog日志服务器
CentOS 7 搭建rsyslog日志服务器 前言一、IP地址及主机名称规划1.修改主机名 二、配置rsyslog日志服务器1.安装rsyslog服务2.编辑/etc/rsyslog.conf 文件3.启动并启用rsyslog服务4.验证端口是否侦听 三、在rsyslog日志服务器上配置firewalld防火墙四、配置rsyslog日志客户端1.编…...
使用Spring Boot Actuator监控应用健康状态
使用Spring Boot Actuator监控应用健康状态 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨如何利用Spring Boot Actuator来监控和管理应用程序的…...
leetcode刷题:vector刷题
🔥个人主页:guoguoqiang. 🔥专栏:leetcode刷题 1.只出现一次的数字 这道题很简单,我们只需要遍历一次数组即可通过异或运算实现。(一个数与自身异或结果为0,任何数与0异或还是它本身) class Solut…...
CGI面试题及参考答案
什么是CGI?它在Web服务器与应用程序之间扮演什么角色? CGI(Common Gateway Interface) 是一种标准协议,它定义了Web服务器与运行在服务器上的外部程序(通常是脚本或应用程序)之间的通信方式。简单来说,CGI充当了一个桥梁,使得Web服务器能够将用户的请求传递给后端程序…...
论文调研_物联网漏洞检测综述
A Review of IoT Firmware Vulnerabilities and Auditing Techniques 研究背景:物联网设备在工业、消费类等各个领域得到了广泛应用,实现了更高的自动化和生产率。然而,这些连网设备的高度依赖也带来了一系列网络安全威胁,特别是…...
Java学习【IO流:深入理解与应用(上)】
Java学习【IO流:深入理解与应用(上)】 🍃1.IO流体系结构🍃2.FileOutputStream🍁2.1FileOutputStream写数据的三种方式🍁2.2换行和续写 🍃3.FileInputStream🍁3.1每次读取…...
干货系列:SpringBoot3第三方接口调用10种方式
环境:SpringBoot.3.3.0 1、简介 在项目中调用第三方接口是日常开发中非常常见的。调用方式的选择通常遵循公司既定的技术栈和架构规范,以确保项目的一致性和可维护性。无论是RESTful API调用、Feign声明式HTTP客户端、Apache HttpClient等调用方式&…...
KVM性能优化之CPU优化
1、查看kvm虚拟机vCPU的QEMU线程 ps -eLo ruser,pid,ppid,lwp,psr,args |awk /^qemu/{print $1,$2,$3,$4,$5,$6,$8} 注:vcpu是不同的线程,而不同的线程是跑在不同的cpu上,一般情况,虚拟机在运行时自身会点用3个cpus,为保证生产环…...
lua中判断2个表是否相等
当我们获取 table 长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度,而且还会出现奇怪的现象。例如:t里面有3个元素,但是因为最后一个下表是5和4,却表现出不一…...
uni-app 自定义支付密码键盘
1.新建组件 payKeyboard .vue <template><view class"page-total" v-show"isShow"><view class"key-list"><view class"list" v-for"(item,index) in keyList" :class"{special:item.keyCode190…...
抖音微短剧小程序源码搭建:实现巨量广告数据高效回传
在数字化营销日益盛行的今天,抖音微短剧小程序已成为品牌与观众互动的新渠道。这些短小精悍的剧目不仅能迅速抓住用户的注意力,还能有效提升品牌的知名度和用户黏性。然而,想要充分利用这一营销工具,关键在于如何高效地追踪广告数…...
springboot数字化医院产科系统源码
目录 一、系统概述 二、开发环境 三、功能设计 四、功能介绍 一、系统概述 数字化产科是为医院产科量身定制的信息管理系统。它管理了孕妇从怀孕开始到生产结束42天一系列医院保健服务信息。该系统由门诊系统、住院系统、数据统计模块三部分组成,与医院HIS、LI…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
