概述

maven-mvnd,可简称(或缩写)mvnd,the Maven Daemon。Apache Maven团队借鉴Gradle和Takari后开发的更快的构建工具。mvnd内嵌Maven,开发者可无缝从Maven迁移到mvnd。

参考资料:GitHub

mvnd中会启动一个或多个守护进程,用来执行实际的构建服务。一个守护进程实例可以为来自mvnd客户端的多个连续请求提供Maven构建服务。当没有空闲的守护进程时来支撑构建请求时,mvnd可以并行生成多个守护程序。

mvnd使用GraalVM代替传统的JVM,GraalVM启动速度更快,占用的内存更少,在实行构建时不需要为每个构建启动新的JVM。GraalVM的JIT(Just In Time)实时编译特性也被运用到Maven构建作业中。JIT可大大减少编译时间,在重复构建过程中,JIT优化代码立即可用,也极大提高构建效率。

另外Maven插件在构建时不再需要多次加载,而是缓存在多个构建中。SNAPSHOT版本的Maven插件不会被缓存。

默认情况下,mvnd使用多个CPU内核并行构建模块。使用的内核数公式Math.max(Runtime.getRuntime().availableProcessors() - 1, 1)。如果代码不支持并行构建,在命令行添加-T1参数切换到串行构建。

相比Maven,mvnd的优势:

  • 运行构建的JVM不需要为每个构建重新启动;
  • Maven插件类的类加载器缓存在多个构建中,插件jars只会被读取和解析一次;
  • JVM中JIT生成的本机代码会被保留。与Maven相比,JIT编译花费的时间更少。在重复构建期间,JIT优化的代码立即可用。这不仅适用于来自Maven插件和Maven内核的代码,也适用于来自JDK本身的所有代码。

安装使用

下载地址,读者根据各自的操作系统选择。本文选择Windows系统1.0.1版本,这里选择下载zip压缩包(另外还有tar.gz压缩包)。解压缩后,将目录名maven-mvnd-1.0.1-windows-amd64重命名为mvnd-1.0.1

目录如下:

比Maven快2~10倍的编译工具mvnd简介与实战_java


进入到mvn目录下,发现和之前的Maven目录设置没有区别:

比Maven快2~10倍的编译工具mvnd简介与实战_守护进程_02


配置环境变量和之前使用Maven没有区别,过程略。与Maven的命令完全相同,改为mvnd即可:

比Maven快2~10倍的编译工具mvnd简介与实战_java_03


mvnd与Maven互不干扰。

默认情况下,Maven下载的JAR包存放在C盘目录下,但C盘一般都是SSD,空间有限。于是会修改Maven的settings.xml配置文件,将JAR包放到D盘:

<localRepository>D:\maven\repository</localRepository>

在Maven项目工程里,即pom.xml文件所在目录里执行命令:mvnd clean -Dquickly,却发现mvnd在下载各种依赖JAR包,这些不应该早就下载好了吗?

比Maven快2~10倍的编译工具mvnd简介与实战_maven_04


发现C盘.m2目录下,确实多了个repository目录。与此同时,发现还有另一个目录:

比Maven快2~10倍的编译工具mvnd简介与实战_守护进程_05


看到这个daemon日志文件,也印证着上文所述:mvnd中会启动一个或多个守护进程。

想到在使用Maven时,需配置settings.xml文件。mvnd是不是也需要配置文件?

于是,找到mvnd安装目录conf下的mvnd.properties配置文件。分析此文件,不难发现最后一个配置项maven.settings,用于指定settings.xml文件路径:

# The location of the maven settings file. The client normally uses default settings in {@code ~/.m2/settings.xml}.
# maven.settings=<path>

于是删除注释符,修改配置为:maven.settings=D:\maven\settings.xml,再次执行命令mvnd clean -Dquickly,结果报错:

比Maven快2~10倍的编译工具mvnd简介与实战_maven_06


分析报错,缺少转义字符?

尝试修改配置为:maven.settings=D:\\maven\\settings.xml,执行成功。

性能对比

Maven官宣,mvnd比Maven编译速度快。

此处暂时给不出严谨的对比数据,但是可以执行两条命令来验证一下是否如此(当然截图也不一定就肯定正确)。

另外,如上面某个截图所示,我使用的Maven版本为3.6.3,不算低(好吧,最新版是3.9.9,有点低了);mvnd版本为1.0.1,是最新版。

首先看clean -Dquickly命令。

某一次mvn clean -Dquickly执行结果截图:

比Maven快2~10倍的编译工具mvnd简介与实战_java_07


耗时0.69s。不明白为何此处使用maven-clean-plugin2.5这么低的插件版本。某一次mvnd clean -Dquickly执行结果截图:

比Maven快2~10倍的编译工具mvnd简介与实战_maven_08


耗时0.076s!是mvn clean命令耗时的10%。另外,多了个括号,Wall Clock。

再来看compile -Dquickly命令。

某一次mvn compile -Dquickly执行结果截图:

比Maven快2~10倍的编译工具mvnd简介与实战_maven_09


编译过程出现两个插件maven-resources-pluginmaven-compiler-plugin,耗时3.867s。某一次mvnd compile -Dquickly执行结果截图:

比Maven快2~10倍的编译工具mvnd简介与实战_java_10


编译耗时0.996,是mvn compile命令耗时的26%。另外上图编译过程中出现乱码。

声明:截图都是某一次执行结果。

事实上,反复多次执行,完全可以得出mvnd比mvn快不止2倍的结论。另外,mvnd.properties文件里还有很多配置项可以优化编译过程。

Wall Clock

参考Wall Clock and Monotonic Clock

Wall Clock就是一般意义上的时间。

Monotonic Clock,单调时间,实际上它指的是从某个点开始后(如系统启动以后)流逝的时间,一定是单调递增的。

计算两个时间点的差值一定要用Monotonic Clock,因为Wall clock是可以被修改的,如计算机时间被回拨(比如校准或者人工回拨等情况),或闰秒(Leap Second),会导致两个Wall Clock可能出现负数。

因为操作系统或上层应用不一定完全支持闰秒,出现闰秒后系统时间会在后续某个点会调整为正确值,就有可能出现时钟回拨。当然也不一定,比如ntpdate就有可能出现时钟回拨,但ntpd就不会。

中文乱码

理论上,IDEA里Terminal控制台,mvn或mvnd命令基于如下maven-compiler-plugin配置:

<build>
	<pluginManagement>
		<plugins>
			<plugin>
			<!--by default groupId is org.apache.maven.plugins-->
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.13.0</version>
				<configuration>
					<source>22</source>
					<target>22</target>
					<encoding>UTF-8</encoding>
					<showWarnings>true</showWarnings>
				</configuration>
			</plugin>
		</plugins>
	</pluginManagement>
</build>

而这个配置是没有问题的,为啥mvnd还是会出现中文乱码呢?

反复查看mvnd.properties配置文件,也没找到跟编码相关的配置项。

TODO:待解决。

现在时间:2024年8月24日01:59:18。老命要紧

日志解读

C盘目录下,类似于daemon-cbaabd83.log日志文件:

Dispatch message: KeepAlive
Dispatch message: ProjectStopped{payload='yaml_groovy'}
Dispatch message: BuildFinished{exitCode=0}
No more message to dispatch
Daemon back to idle
Updating state to: Idle

比Maven快2~10倍的编译工具mvnd简介与实战_java_11


然后在日志文件里发现如上图所示,也就是说mvnd命令没有问题,IDEA的Terminal控制台展示中文乱码?展示mvn命令正常?

daemon-cbaabd83.out.log日志文件:

Starting daemon process: id = 8284f806, workingDir = E:\Java\maven\mvnd-1.0.1, daemonArgs: -classpath

都是些命令启动参数。