GraalVM(云原生时代的Java)和IoT在边缘侧落地与实践
环顾四周,皆是对手!
- 云时代的掉队者,由于Java启动的高延时、对资源的高占用、导致在Serverless及FaaS架构下力不从心,在越来越流行的边缘计算、IoT方向上也是难觅踪影;
- Java语言在业务服务开发中孤独求败,但在系统级应用领域几乎是C、C++、搅局者Go、黑天鹅Rust的天下;
- 移动应用、敏捷应用的追随者,移动应用中Android逐步去Java,前端又是JS的世界,敏捷开发方面前有Ruby、Python后有NodeJS;
此时众多的Javaer会不经意发问:学Java还有未来么?
你可以嫌弃Java, 但是可以永远相信JVM! 在云原生如日中天、Serverless日渐成熟、新语言百花齐放的当下,跨语言、Native支持、高性能低资源占用的技术必定是其璀璨的明珠,而GraalVM正是这样一个承载了JVM未来,将Java带入下一波技术的弄潮儿。
GraalVM - 云原生时代的Java
“一次编写,到处运行“是Java语言的特性,这一重要特性依靠的是JVM虚拟机
在讨论GraalVM之前,我们先聊下Java代码是怎么运行的?
从硬件视角来看,Java字节码无法直接执行。因此,Java虚拟机需要将字节码翻译成机器码。
从虚拟机视角来看,执行Java代码首先需要将它编译而成的 class 文件加载到 Java 虚拟机中。加载后的 Java 类会被存放于方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。Java 虚拟机同样也在内存中划分出堆和栈来存储运行时数据。
不同的是,Java 虚拟机会将栈细分为面向 Java 方法的 Java 方法栈,面向本地方法(用 C++ 写的 native 方法)的本地方法栈,以及存放各个线程执行位置的 PC 寄存器。
在运行过程中,每当调用进入一个 Java 方法,Java 虚拟机会在当前线程的 Java 方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且 Java 虚拟机不要求栈帧在内存空间里连续分布。
当退出当前执行的方法时,不管是正常返回还是异常返回,Java 虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。
GraalVM带来哪些神奇的黑魔法♂️
更快、更轻量化的应用
GraalVM的高性能JIT编译器可以生成优化的本地机器代码,由于采用了先进的编译器优化和积极复杂的内联技术,运行速度更快,产生的垃圾更少,使用的CPU更少。最终的结果是应用程序运行速度更快,消耗的资源更少,从而降低了云和基础设施的成本。
Ahead-Of-Time(AOT)提前编译技术
AOT 提前编译,是相对于即时编译而言的。AOT在运行过程中耗费 CPU 资源来进行即时编译,而程序也能够在启动的瞬间就达到理想的性能。例如 C 和 C++语言采用的是AOT静态编译,直接将代码转换成机器码执行。而 Java 一直采用的是解释 + 即时编译技术。
GraalVM 的 AOT 编译实际上是借助了 SubstrateVM 编译框架,可以将 SubstrateVM 理解为一个内嵌精简版的 JVM,包含异常处理,同步,线程管理,内存管理(垃圾回收)和 JNI 等组件。
SubstrateVM 的启动时间非常短,内存开销非常少。用这种方式编译出的 Java 程序的执行时间可与C语言持平。
语言互操作性
Graal VM 被官方称为“Universal VM”和“Polyglot VM”,这是一个在 HotSpot 虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用,这里“任何语言”包括了 Java、Scala、Groovy、Kotlin 等基于 Java 虚拟机之上的语言,还包括了 C、C++、Rust 等基于 LLVM 的语言,同时支持其他像 JavaScript、Ruby、Python 和 R 语言等等。Graal VM 可以无额外开销地混合使用这些编程语言,支持不同语言中混用对方的接口和对象,也能够支持这些语言使用已经编写好的本地库文件。
GraalVM在IoT边缘侧实现落地,效果显著!
1、安装无需JVM环境,30000行项目安装仅需16M内存占用,可在mac、linux-arm64、windows和树莓派等硬件环境上运行;
2、项目启动时间节省99%, 仅需10ms!享受极速的软件体验;
安装
GraalVM在 SDKMAN上有多个版本,可通过sdk list java进行查看,我这里使用的是22.2 社区版本
sdk install java 22.2.r11-grl
上述GraalVM安装完成后,需要Native Image组件,使用下述命令行进行安装:
Native Image安装
gu install native-image
Maven项目设置
详细graalvm-maven-plugin使用简介见:https://graalvm.github.io/native-build-tools/latest/maven-plugin-quickstart.html
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${graalvm-buildtools.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<skip>false</skip>
<imageName>${appName}</imageName>
<requiredVersion>${graalvm.version}</requiredVersion>
<mainClass>${mainClass}</mainClass>
<buildArgs>
<arg>--no-fallback</arg>
<arg>-Dfile.encoding=UTF-8</arg>
<arg>-H:-CheckToolchain</arg>
<arg>-H:+ReportExceptionStackTraces</arg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
当然GraalVM的 AOT提前编译技术也不是完全没有缺点,其对于多语言、反射和字节码动态加载技术需要相对繁琐的配置。
在src/main/resources文件夹下有
- native-image.properties //native image配置信息文件
- reflection-config.json //native 需要反射的类配置文件
具体配置信息详情见: https://www.graalvm.org/22.0/reference-manual/native-image/Reflection/#manual-configuration
native-image.properties信息如下:
Args = -H:ReflectionConfigurationResources=${.}/reflection-config.json \
-H:IncludeLocales=zh,en,de,fr \
--initialize-at-run-time=io.netty.buffer.AbstractReferenceCountedByteBuf \
--initialize-at-run-time=io.netty.buffer.ByteBufAllocator \
--initialize-at-run-time=io.netty.buffer.ByteBufUtil \
--initialize-at-run-time=io.netty.buffer.ByteBufUtil$HexUtil \
--initialize-at-run-time=io.netty.buffer.PooledByteBufAllocator \
--initialize-at-run-time=io.netty.buffer.UnpooledHeapByteBuf \
--initialize-at-run-time=io.netty.buffer.UnreleasableByteBuf \
--initialize-at-run-time=io.netty.util.AbstractReferenceCounted \
--initialize-at-run-time=io.netty.util.internal.ThreadLocalRandom \
--initialize-at-run-time=io.netty.util.concurrent.GlobalEventExecutor \
--initialize-at-run-time=io.netty.util.concurrent.ImmediateEventExecutor \
--initialize-at-run-time=io.netty.util.concurrent.ScheduledFutureTask \
-Dio.netty.noUnsafe=true \
-Dio.netty.leakDetection.level=DISABLED
reflection-config.json信息如下:
[
{
"name": "io.netty.channel.socket.nio.NioSocketChannel",
"methods": [
{ "name": "<init>", "parameterTypes": [] }
]
},
......
{
"name" : "iot.technology.client.toolkit.nb.service.mobile.domain.action.data.MobCachedCommandResponse",
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"allDeclaredClasses" : true,
"allPublicClasses" : true,
"allDeclaredConstructors" : true,
"allPublicConstructors" : true
}
]
上述信息配置完成,我们在项目根目录下执行以下命令:
mvn clean package -Pnative
会见到下面的日志命令输出:
[INFO] --- native-maven-plugin:0.9.17:compile-no-fork (build-native) @ toolkit-app ---
[INFO] Found GraalVM installation from JAVA_HOME variable.
......
========================================================================================================================
GraalVM Native Image: Generating 'toolkit' (executable)...
========================================================================================================================
[1/7] Initializing... (9.2s @ 0.20GB)
Version info: 'GraalVM 22.2.0 Java 17 CE'
Java version info: '17.0.4+8-jvmci-22.2-b06'
C compiler: cc (null, null, 0.0.0)
Garbage collector: Serial GC
1 user-specific feature(s)
- org.graalvm.home.HomeFinderFeature: Finds GraalVM paths and its version number
[2/7] Performing analysis... [*********] (45.3s @ 2.77GB)
11,123 (90.04%) of 12,354 classes reachable
18,642 (59.17%) of 31,506 fields reachable
57,906 (57.83%) of 100,137 methods reachable
524 classes, 194 fields, and 2,046 methods registered for reflection
79 classes, 196 fields, and 136 methods registered for JNI access
5 native libraries: -framework CoreServices, -framework Foundation, dl, pthread, z
[3/7] Building universe... (4.7s @ 1.45GB)
[4/7] Parsing methods... [**] (3.6s @ 1.88GB)
[5/7] Inlining methods... [***] (2.2s @ 3.94GB)
[6/7] Compiling methods... [*****] (31.6s @ 4.19GB)
[7/7] Creating image... (5.6s @ 2.77GB)
25.79MB (49.67%) for code area: 37,581 compilation units
25.84MB (49.78%) for image heap: 290,531 objects and 175 resources
292.13KB ( 0.55%) for other data
51.92MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area: Top 10 object types in image heap:
1.65MB sun.security.ssl 5.53MB byte[] for code metadata
1021.89KB java.util 2.78MB java.lang.String
919.87KB picocli 2.73MB java.lang.Class
731.85KB com.sun.crypto.provider 2.62MB byte[] for general heap data
565.20KB java.lang.invoke 2.42MB byte[] for java.lang.String
518.23KB java.lang 1.46MB byte[] for embedded resources
504.26KB org.jline.reader.impl 955.88KB com.oracle.svm.core.hub.DynamicHubCompanion
476.05KB c.s.org.apache.xerces.internal.impl.xs.traversers 663.69KB byte[] for reflection metadata
458.58KB sun.security.x509 623.30KB java.util.HashMap$Node
438.98KB com.sun.org.apache.xerces.internal.impl 580.77KB java.lang.String[]
18.36MB for 389 more packages 4.51MB for 2478 more object types
------------------------------------------------------------------------------------------------------------------------
6.3s (5.7% of total time) in 32 GCs | Peak RSS: 5.93GB | CPU load: 5.50
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
......
GraalVM带来的惊人效果
启动时间
启动时间代码添加:
public static void main(String[] args) {
long begin = System.currentTimeMillis();
......
try {
......
long end = System.currentTimeMillis();
long runTime = end - begin;
System.out.println("startUp cost runTime: " + runTime);
......
} finally {
......
}
}
使用IDEA启动代码,启动时间如下:
AOT提前编译后native-image启动时间:
➜ target git:(develop-v0.6.6) ✗ ./toolkit
......
startUp cost runTime: 11
占用内存的效果
项目代码统计:
编译后可在linux、树莓派、mac系统和windows系统可运行的镜像;
上述的项目配置和落地实践项目见: https://github.com/IoT-Technology/IoT-Toolkit
结束语
GraalVM对于JVM的开发人员来说,无疑是个非常好的消息。但是留给JVM的时间真的不多了。
GraalVM(云原生时代的Java)和IoT在边缘侧落地与实践的更多相关文章
- 🏆【JVM深层系列】「云原生时代的Java虚拟机」针对于GraalVM的技术知识脉络的重塑和探究
GraalVM 背景 新.旧编程语言的兴起躁动,说明必然有其需求动力所在,譬如互联网之于JavaScript.人工智能之于Python,微服务风潮之于Golang等等.大家都清楚不太可能有哪门语言能在 ...
- 云原生时代,Java的危与机(周志明)
说明 本篇文章是转载自周志明老师的文章,链接地址:https://www.infoq.cn/article/RQfWw2R2ZpYQiOlc1WBE 今天,25 岁的 Java 仍然是最具有统治力的编 ...
- 云原生时代的Java
原文链接(作者:周志明):https://time.geekbang.org/column/article/321185 公开课链接:https://time.geekbang.org/opencou ...
- 初步探索GraalVM——云原生时代JVM黑科技
1 云原生时代Java语言的困境 经过多年的演进,Java语言的功能和性能都在不断的发展和提高,诸如即时编译器.垃圾回收器等系统都能体现Java语言的优秀,但是想要享受这些功能带来的提升都需要一段时间 ...
- 云原生时代高性能Java框架—Quarkus(一)
--- Quarkus&GraalVM介绍.创建并启动第一个项目 Quarkus系列博文 Quarkus&GraalVM介绍.创建并启动第一个项目 构建Quarkus本地镜像.容器化部 ...
- 云原生时代高性能Java框架—Quarkus(二)
--- *构建Quarkus本地镜像.容器化部署Quarkus项目* Quarkus系列博文 Quarkus&GraalVM介绍.创建并启动第一个项目 构建Quarkus本地镜像.容器化部署Q ...
- 进击的 Java ,云原生时代的蜕变
作者| 易立 阿里云资深技术专家 导读:云原生时代的来临,与Java 开发者到底有什么联系?有人说,云原生压根不是为了 Java 存在的.然而,本文的作者却认为云原生时代,Java 依然可以胜任&qu ...
- 云原生时代 给予.NET的机会
.NET诞生于与Java的竞争,微软当年被罚款20亿美元. Java绝不仅仅是一种语言,它是COM的替代者! 而COM恰恰是Windows的编程模型.而Java编程很多时候比C++编程要容易的多,更致 ...
- 云原生时代的DevOps平台设计之道
开发人员与运维人员是 IT 领域很重要的两大人群,他们都会参与到各种业务系统的建设过程中去.DevOps 是近年间火爆起来的一种新理念,这种理念被很多人错误的解读为"由开发人员(Dev)学习 ...
- 进击的.NET 在云原生时代的蜕变
你一定看过这篇文章 <进击的 Java ,云原生时代的蜕变>, 本篇文章的灵感来自于这篇文章.明天就将正式发布.NET Core 3.0, 所以写下这篇文章让大家全面认识.NET Cor ...
随机推荐
- 解决git仓库项目 添加到github非空仓库冲突问题 error: failed to push some refs to 'https://github.com/Qtoken/......'
error: failed to push some refs to 'https://github.com/Qtoken/......' 1. 问题描述:执行命令:git push origin m ...
- npm proxy问题
检查你的电脑是否需要配置代理,如果不需要可以将代理禁用: npm config set proxy false 如果是需要配置代理服务的: 开启代理 npm config set proxy true ...
- java学习笔记(四)变量
局部变量,必须声明和初始化值: 实列变量,从属于对象:如果不自行初始化,这个类型的默认值,数值类型,0,0.0 布尔值 默认为false 除了基本类型下,其余的默认值都是null 如 变量类型 ...
- Flink学习系列——简介
Flink起源 德国柏林 Flink的目标 低延迟 高吞吐 较高的准确性(乱序数据的处理) 良好的容错性(容错性差的表现:一个节点挂了,全部回滚重新做计算,这对实时性要求高的场景非常致命)
- 快速排序(Java分治法)
快速排序(Java分治法) 文章目录 快速排序(Java分治法) 0. 分治策略 1.思路步骤 2.代码 3.复杂度分析 3.1 最好情况 3.2 最坏情况 3.3 平均情况 3.4 性能影响因素 4 ...
- STM32使用DMA接收不定长数据
开启串口,是能串口全局中断 配置DMA并勾选Memory选项 继续配置工程并且生成代码 添加一些串口通讯使用的全局变量 #define BUFFER_SIZE 128 uint8_t Tx_Buf[5 ...
- 微信小程序分享出去的页面再点进来,如何取值并且在新用户未授权的情况下,授权后跳到当前页面
1.如何点击分享的页面进来,授权后跳转到当前页面 可以在授权成功后,将openid.头像.昵称入库成功之后,标记一下,及getStorageSync // 通过code获取openid getUser ...
- c#调用键盘输入
[code]csharpcode: /// <summary> /// 键盘输入模拟 /// </summary> [DllImport("user32.dll&qu ...
- vue2+element表格拖拽
1.定义好拖拽元素 ref标识,以及 row-key="id" (row-key拖拽标识,拖拽后数据不会乱, id为tableDataNew数据对象id) 2.下载cnpm in ...
- Blazor项目在VisualStudio调试时配置运行基础目录
最近在使用 Blazor 开发管理后台时遇到了如下的问题,我这里后台整体采用了 AntDesignBlazor 组件库,在上线之后发现ReuseTabs组件在使用过程中,如果默认 / 没有指定为项目的 ...