首页IT科技jvm中gc算法(过两年 JVM 可能就要被 GraalVM 替代了)

jvm中gc算法(过两年 JVM 可能就要被 GraalVM 替代了)

时间2025-05-05 00:56:37分类IT科技浏览3925
导读:大家好,我是风筝,公众号「古时的风筝」,专注于 Java技术 及周边生态。...

大家好           ,我是风筝                ,公众号「古时的风筝」     ,专注于 Java技术 及周边生态           。

文章会收录在 JavaNewBee 中      ,更有 Java 后端知识图谱                ,从小白到大牛要走的路都在里面                。

今天说一说 GraalVM     。

GraalVM 是 Oracle 大力发展和想要推广的新一代 JVM           ,目前很多框架都已经渐渐支持 GraalVM 了      ,比如我们在用的 Spring 也已经推出了对 GraalVM 兼容的工具包了           。

既然说的这么厉害                ,那么它到底是何方神圣呢                 。

GraalVM 和 JVM 的关系

既然叫做VM           ,那肯定和 JVM 有关系的吧     。JVM 全称 Java 虚拟机,我们都知道                ,Java 程序是运行在虚拟机上的                ,虚拟机提供 Java 运行时,支持解释执行和部分的(JIT)即时编译器           ,并且负责分配和管理 Java 运行所需的内存                ,我们所说的各种垃圾收集器都工作在 JVM 中     。

比如 Oracle JDK           、OpenJDK      ,默认的 JVM 是 HotSpot 虚拟机           ,这是当前应用最广泛的一个虚拟机                 。我们平时见到的各种将虚拟机的书籍                、文章     、面试题                ,基本上都是说的 HotSpot 虚拟机           。

除此之外     ,还有一些商用      ,或者说小众的虚拟机存在                ,比如IBM 的J9 JVM           ,商用的 Zing VM 等     。

那 GraalVM 是另一种 Java 虚拟机吗?

是      ,又不全是                。

GraalVM 可以完全取代上面提到的那几种虚拟机                ,比如 HotSpot           。把你之前运行在 HotSpot 上的代码直接平移到 GraalVM 上           ,不用做任何的改变,甚至都感知不到                ,项目可以完美的运行。

但是 GraalVM 还有更广泛的用途                ,不仅支持 Java 语言,还支持其他语言                。这些其他语言不仅包括嫡系的 JVM 系语言           ,例如 Kotlin           、Scala                ,还包括例如 JavaScript                 、Nodejs     、Ruby     、Python 等                。

GraalVM 的野心不止于此     ,看上面的图           ,它的目的是搭建一个 Framework                ,最终的目标是想要支持任何一种语言     ,无论哪种语言      ,可以共同跑在 GraalVM 上                ,不存在跨语言调用的壁垒。

GraalVM 和JDK有什么关系

Java 虚拟机都是内置在 JDK 中的           ,比如Orcale JDK                 、OpenJDK      ,默认内置的都是 HotSpot 虚拟机           。

GraalVM 也是一种 JDK                ,一种高性能的 JDK                。完全可以用它替代 OpenJDK           、Orcale JDK     。

GraalVM 如何运行 Java 程序

说了半天           ,是不是还是不知道 GraalVM 到底是什么           。

GraalVM - 还包含 Graal (JIT)即时编译器,可以结合 HotSpot 使用

GraalVM – 是一种高性能 JDK                ,旨在加速 Java 应用程序性能                ,同时消耗更少的资源                 。

GraalVM - 是一种支持多语言混编的虚拟机程序,不仅可以运行 JVM 系列的语言           ,也可支持其他语言     。

GraalVM 提供了两种方式来运行 Java 程序     。

第一种:结合 HotSpot 使用

上面说了                ,GraalVM 包含 Graal (JIT)即时编译器     ,自从 JDK 9u 版本之后           ,Orcale JDK 和 OpenJDK 就集成了 Graal 即时编译器                 。我们知道 Java 既有解释运行也有即时编译           。

当程序运行时                ,解释器首先发挥作用     ,代码可以直接执行     。随着时间推移      ,即时编译器逐渐发挥作用                ,把越来越多的代码编译优化成本地代码           ,来获取更高的执行效率                。即时编译器可以选择性地编译热点代码      ,省去了很多编译时间                ,也节省很多的空间           。比如多次执行的方法或者循环     、递归等。

JDK 默认使用的是 C2 即时编译器           ,C2是用C++编写的                。而使用下面的参数可以用 Graal 替换 C2                。

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

Graal 编译器是用 Java 实现的,用 Java 实现自己的编译器。Graal 基于一些假设的条件                ,采取更加激进的方式进行优化           。采用 Graal 编译器之后                ,对性能有会有一定的提升                。

但是如果你还是在用 JDK8,那对不起了           ,GraalVM 的一切都用不了     。

第二种:AOT 编译本地可执行程序

这是 GraalVM 真正厉害的地方           。

AOT 提前编译                ,是相对于即时编译而言的                 。AOT在运行过程中耗费 CPU 资源来进行即时编译     ,而程序也能够在启动的瞬间就达到理想的性能     。例如 C 和 C++语言采用的是AOT静态编译           ,直接将代码转换成机器码执行     。而 Java 一直采用的是解释 + 即时编译技术                ,大多数情况下 Java 即时编译的性能并不比静态编译差     ,但是还是一直朝着 AOT 编译的方向努力                 。

但是 Java 对于 AOT 来说有一些难点      ,比如类的动态加载和反射调用           。

GraalVM 显然是已经克服了这些问题                ,使用 GraalVM 可以直接将 Java 代码编译成本地机器码形态的可执行程序     。

我们目前运行 Java 一定要安装 JDK 或者 JRE 对不对           ,如果将程序直接编译成可执行程序      ,就不用在服务器上安装 JDK 或 JRE 了                。那就是说运行 Java 代码其实也可以不用虚拟机了是吗?

GraalVM 的 AOT 编译实际上是借助了 SubstrateVM 编译框架                ,可以将 SubstrateVM 理解为一个内嵌精简版的 JVM           ,包含异常处理,同步                ,线程管理                ,内存管理(垃圾回收)和 JNI 等组件           。

SubstrateVM 的启动时间非常短,内存开销非常少。用这种方式编译出的 Java 程序的执行时间可与C语言持平                。

下图是使用即时编译(JVM运行)与 AOT (原生可执行程序)两种方式的 CPU 和内存使用情况对比           ,可以看出来                ,AOT 方式下 CPU 和内存的使用都非常少                。

除了运行时占用的内存少之外     ,用这种方式最终生成的可执行文件也非常小。这对于云端部署非常友好           。目前很多场景下都使用 Docker 容器的方式部署           ,打一个 Java 程序的镜像包要包含完整的 JVM 环境和编译好的 Jar 包                。而AOT 方式可以最大限度的缩小 Docker 镜像的体积     。

缺点

好处多多                ,当然也有一些弊端           。对于反射这种纯粹在运行时才能确定的部分     ,不可能完全通过优化编译器解决      ,只能通过增加配置的方式解决                 。麻烦是麻烦了一点                ,但是是可行的           ,Spring Boot 2.7的版本已经支持原生镜像了      ,Spring 这种非常依赖反射的框架都可以支撑                ,我们用起来也应该没问题     。

GraalVM 如何支持多语言

要支持多语言           ,就要说到 GraalVM 中的另一个核心组件 Truffle 了     。

Truffle 是一个用 Java 写就的语言实现框架                 。基于 Truffle 的语言实现仅需用 Java 实现词法分析                、语法分析以及针对语法分析所生成的抽象语法树(Abstract Syntax Tree,AST)的解释执行器                ,便可以享用由 Truffle 提供的各项运行时优化           。

就一个完整的 Truffle 语言实现而言                ,由于实现本身以及其所依赖的 Truffle 框架部分都是用 Java 实现的,因此它可以运行在任何 Java 虚拟机之上     。

当然           ,如果 Truffle 运行在附带了 Graal 编译器的 Java 虚拟机之上                ,那么它将调用 Graal 编译器所提供的 API     ,主动触发对 Truffle 语言的即时编译           ,将对 AST 的解释执行转换为执行即时编译后的机器码                。

目前除了 Java                , JavaScript           、Ruby、Python 和许多其他流行语言都已经可以运行在 GraalVM 之上了           。

GraalVM 官方还提供了完整的文档     ,当有一天你开发了一款新的语言      ,也可以用 Truffle 让它跑在 GraalVM 上。

安装和使用

GraalVm 目前的最新版本是 22.3                ,分为社区版和企业版           ,就好像 OpenJDK 和 商用的 Orcale 的 JDK       ,企业版会多一些性能分析的功能                ,用来帮助更大程度的优化性能                。

社区版是基于OpenJDK 11.0.17, 17.0.5, 19.0.1           ,而商业版基于Oracle JDK 8u351, 11.0.17, 17.0.5, 19.0.1,所以                ,如果你想用免费的                ,只能将程序升级到 JDK 11 以上了                。

GraalVM 支持 Windows                、Linux                、MacOS ,可以用命令安装最新版           ,或者直接下载对应 Java 版本的。

我是下载的 Java 11 的版本                ,下载下来的压缩包     ,直接解压           ,然后配置环境变量           。把解压目录配置到环境变量的 JAVA_HOME就可以了                。

解压好其实就相当于安装完毕了                ,查看一下版本     。

进入到解压目录下的bin目录中     ,运行 java -version           。运行结果如下:

运行代码

常用方式运行

也就是我们平时一直在用的这种方式      ,把 GrralVM 当做 OpenJDK 使用                ,只不过把即时编译器换成了 Graal                 。就是前面说的第一种方式     。

安装完成后           ,就可以把它当做正常的 JDK 使用了      ,比如 javac、jps           、jmap等都可以直接用了     。大多数人还是用 IDEA 的                ,所以就直接在 IDEA 中使用就好了                 。

1                、先随意创建一个 Java 项目           。

2     、创建完成后           ,打开项目设置     。

3           、在打开的项目设置弹出框中选择 SDKs,点击加号                ,选择前面解压的 GraalVM 目录                。

4                 、然后选择刚刚添加的这个 JDK           。

5     、最后运行一段测试代码。

public class HelloWorld { public static void main(String[] args) throws Exception { System.out.println("Hello GraalVM!"); Thread.sleep(1000 * 100 * 100); } }

上面这样的运行方式                ,其实就相当于前面说的第一种运行方式

native-image 方式运行

这种方式就是 AOT 编译成机器码,已可执行文件的形式出现                。native-image 可以命令行的形式执行           ,也可以在配合 Maven 执行                ,我这儿就直接演示用 Maven 形式的了     ,毕竟IDEA 搭配 Maven 用习惯了                。

1     、安装native-image 工具包

native-image 是用来进行 AOT 编译打包的工具           ,先把这个装上                ,才能进行后面的步骤。

安装好 GraalVM 后     ,在 bin目录下有一个叫做 gu的工具      ,用这个工具安装                ,如果将 bin目录添加到环境中           ,直接下面的命令安装就行了           。

gu install native-image

如果没有将 bin目录加到环境变量中      ,要进入到 bin目录下                ,执行下面的命令安装                。

./gu install native-image

这个过程可能比较慢           ,因为要去 github 上下载东西,如果一次没成功(比如超时)                ,多试两次就好了     。

2                 、配置 Maven

配置各种版本

<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>${java.specification.version} </maven.compiler.source> <maven.compiler.target>${java.specification.version}</maven.compiler.target> <native.maven.plugin.version>0.9.12</native.maven.plugin.version> <imageName>graalvm-demo-image</imageName> <mainClass>org.graalvm.HelloWorld</mainClass> </properties>

native.maven.plugin.version是要用到的编译为可执行程序的 Maven 插件版本           。

imageName是生成的可执行程序的名称                 。

mainClass是入口类全名称     。

配置 build 插件

<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.0.0</version> <executions> <execution> <id>java-agent</id> <goals> <goal>exec</goal> </goals> <configuration> <executable>java</executable> <workingDirectory>${project.build.directory}</workingDirectory> <arguments> <argument>-classpath</argument> <classpath/> <argument>${mainClass}</argument> </arguments> </configuration> </execution> <execution> <id>native</id> <goals> <goal>exec</goal> </goals> <configuration> <executable>${project.build.directory}/${imageName}</executable> <workingDirectory>${project.build.directory}</workingDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.source}</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.2.2</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>${mainClass}</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>${mainClass}</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </build>

配置 profiles

<profiles> <profile> <id>native</id> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <version>${native.maven.plugin.version}</version> <extensions>true</extensions> <executions> <execution> <id>build-native</id> <goals> <goal>build</goal> </goals> <phase>package</phase> </execution> <execution> <id>test-native</id> <goals> <goal>test</goal> </goals> <phase>test</phase> </execution> </executions> <configuration> <fallback>false</fallback> <buildArgs> <arg>-H:DashboardDump=fortune -H:+DashboardAll</arg> </buildArgs> <agent> <enabled>true</enabled> <options> <option>experimental-class-loader-support</option> </options> </agent> </configuration> </plugin> </plugins> </build> </profile> </profiles>

3           、使用 maven 编译                ,打包成本地可执行程序     。

执行 Maven 命令

mvn clean package

或者

mvn -Pnative -Dagent package

编译打包的过程比较慢,因为要直接编译成机器码           ,所以比一般的编译过程要慢一些                 。看到下面的输入日志                ,说明打包成功了           。

4     、运行可执行程序包     ,打开 target 目录           ,已经看到了graalvm-demo-image可执行程序包了                ,大小为 11.58M     。

然后就可以运行它了     ,进入到目录下      ,执行下面的命令运行                ,可以看到正常输出了                。注意了           ,这时候已经是没有用到本地 JVM 了           。

./graalvm-demo-image Hello GraalVM!

这时候      ,用 jps -l命令已经看不到这个进程了                ,只能通过 ps看了。

总结

虽然我们还没有看到有哪个公司说在用 GraalVM 了           ,但是 Quarkus                、Spring Boot           、Spring等很多的框架都已经支持 GraalVM 的 Native-image 模式,而且在 Orcale 的大力推广下                ,相信不久之后就会出现在更多的产品中                。赶紧体验一下吧                。

如果觉得还不错的话                ,给个推荐吧!

公众号「古时的风筝」,Java 开发者           ,专注 Java 及周边生态。坚持原创干货输出                ,你可选择现在就关注我     ,或者看看历史文章再关注也不迟           。长按二维码关注           ,跟我一起变优秀!

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
anaconda安装python3.5(Anaconda(python,pycharm)半详细安装教程)