GraalVM & Spring Boot初体验

前言

这两天封在家里,一直在琢磨想去把这个博客项目改成微服务的形式。不过就目前而言我的服务器内存放好几个Java进程是吃不消的,原因在于一个独立的JVM所占用的内存资源太吃内存。不过在云原生时代,使用容器化部署,每个服务单独享受一整块逻辑的内存空间,实际上对Java是非常不利的。而且即便有的应用功能非常简单,但是占用的内存和功能完全不在一个量级,这可能也是Java常常为人诟病的一点。不过现在有了GraalVM,似乎一切都变得不一样了。

配置

我下载的版本是GraalVM Community Edition 22.3.0,目前的最新版。大多数人其实更关心Spring项目如何整合这个JVM。根据官网配置,首先应该按照https://www.graalvm.org/22.3/docs/getting-started/macos/进行一些配置,接着在https://www.graalvm.org/22.3/reference-manual/native-image/下载GraalVM的一个组件Native Image:

gu install native-image

下载安装完以后,其实配置就可以了。现在需要到Spring官网https://start.spring.io/去生成一个Demo,注意把GraalVM Native Support DEVELOPER TOOLS这个依赖给选上,最终我的依赖文件长这个样子:

plugins {
	id 'org.springframework.boot' version '2.7.5'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
	id 'java'
	id 'org.springframework.experimental.aot' version '0.12.1'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
	maven { url 'https://repo.spring.io/release' }
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-aop'
	implementation 'org.projectlombok:lombok:1.18.22'
	annotationProcessor 'org.projectlombok:lombok:1.18.22'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

tasks.named('bootBuildImage') {
	builder = 'paketobuildpacks/builder:tiny'
	environment = ['BP_NATIVE_IMAGE': 'true']
}

也不需要你去做什么,基本都自动生成了。我这里用的Spring Boot版本是2.7.5。

当GraalVM把项目构建完以后,在IDEA的Gradle插件的build下面选择nativeCompile

然后开启漫长的编译过程:

编译成功然后在build文件夹下就会有这么一个二进制可执行文件demo:

这个文件可以直接双击启动:

启动用时52毫秒,非常的可以了。

而内存占用呢:

不可能不心动。Java直接变成go了。

补充

如果需要在项目里使用动态代理,需要显示指定需要代理的类,如:

@SpringBootApplication
@AotProxyHint(targetClass=com.example.demo.controller.TestController.class, proxyFeatures = ProxyBits.IS_STATIC)
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

本质是对这个切面的配置:

@Aspect
@Component
@Slf4j
public class TestAspect {

    @Pointcut("execution(* com.example.demo.controller..*.*(..))")
    public void pt() {}

    @SneakyThrows
    @Before("pt()")
    public void before(JoinPoint jp) {
        log.info("java");
    }
}

不显示声明的话启动会报错。原因在于JVM的设计是一个开放的系统,有些类是在运行时加载到虚拟机里的,这个就涉及了Java动态代理的原理。如果把程序编译成二进制的文件脱离了JVM,就丧失了这种开放性,所以只能用这种没有办法的办法将所有需要被代理的类枚举式的声明出来,在构建二进制文件的时候就把这些类提前的构建出来。

posted @ 2022-11-06 16:56  imissinstagram  Views(2531)  Comments(0Edit  收藏  举报