使用 UPX 压缩原生可执行文件

UPX 是一个开源、可移植、高性能的 **可执行文件打包器**,最初创建于 1996 年。它接收一个可执行文件作为输入,并输出一个压缩后的可执行文件。一定 年龄的读者可能很久以前就用过 UPX,那时程序需要压缩才能装到软盘里。二十多年过去了,UPX 依然坚挺。

在 Quarkus 2.6 中,我们在 Quarkus 构建中集成了 UPX 压缩。因此,Quarkus 可以自动生成压缩后的可执行文件。本文将介绍如何使用这个新功能。但在此之前,你需要明白 **天下没有免费的午餐**。压缩后的可执行文件在磁盘上更小,但内存使用量更高。因此,在使用前请务必阅读“重要警告”部分。

准备工作

如果你在系统中安装了 UPX 或使用容器内构建,Quarkus 可以压缩你的可执行文件。

UPX 在大多数操作系统上都可用。因此,你应该能够从 他们的发布页面 下载它。UPX 是跨平台的。因此,即使从 macOS 或 Windows 机器,你也可以压缩 Linux 64 位可执行文件。

如果安装 UPX 不是你的选项,你可以要求 Quarkus 使用容器内构建(通过 -Dquarkus.native.container-build=true)来构建你的原生可执行文件。你将获得一个 Linux 64 位可执行文件(非常适合容器,但如果你不使用 Linux,则在你的机器上无法使用)。容器内构建提供了 UPX,以便它可以压缩可执行文件。使用容器内构建时,你的机器上不需要 GraalVM 或 UPX。此选项在 CI 上特别有用。

在本文中,我们将使用第一种方法。 构建原生可执行文件 页面解释了如何使用容器内构建。

找些东西来压缩

首先,我们需要一个应用程序。为了简单起见,让我们从 https://code.quarkus.io/?a=upx-compression-demo&e=resteasy-reactive-jacksoncode.quarkus.io 创建一个新应用程序。这个应用程序使用了 RESTEasy Reactive 及其 Jackson 支持,但压缩功能适用于任何项目,我们甚至不会查看代码。

在使用 UPX 之前,我们需要测量未压缩的原生可执行文件的磁盘空间和内存使用情况。为此,我们需要原生可执行文件

> ./mvnw package -Dnative

生成的可执行文件占用约 46 MB 的磁盘空间

.rwxr-xr-x 46M clement 11 Dec 17:44 upx-compression-demo-1.0.0-SNAPSHOT-runner

现在,让我们看看内存消耗。使用以下命令启动应用程序

> ./target/upx-compression-demo-1.0.0-SNAPSHOT-runner

在另一个终端中,使用 curl 调用应用程序并获取其内存使用情况

> curl https://:8080/hello
Hello RESTEasy Reactive%

> rss runner
PID 0M COMMAND
3947 21M ./target/upx-compression-demo-1.0.0-SNAPSHOT-runner

因此,它占用了 21 MB 的 RSS,基本上是它使用的 RAM 量。

rss 命令是以下函数
rss () {
  pgrep $1 | xargs ps -o pid,rss,command -p | awk '{$2=int($2/1024)"M";}{ print;}'
}

请参阅 Quarkus - 性能测量 了解更多关于 RSS 和如何测量它的信息

配置压缩

要压缩你的可执行文件,你需要配置压缩级别。压缩级别从 1 到 10

  • 1:更快的压缩

  • 9:更好的压缩

  • 10:最佳压缩(对大文件可能很慢)

application.properties 文件配置级别

quarkus.native.compression.level=7

这就是启用压缩所需的所有操作。

构建压缩后的原生可执行文件

让我们重新生成原生可执行文件。这次,由于配置了压缩级别,Quarkus 将会压缩它

> ./mvnw package -Dnative
...
...
[INFO] [io.quarkus.deployment.pkg.steps.UpxCompressionBuildStep] Executing /usr/local/bin/upx -7 /Users/clement/Downloads/upx-compression-demo/target/upx-compression-demo-1.0.0-SNAPSHOT-runner
 Ultimate Packer for eXecutables
 Copyright (C) 1996 - 2020
 UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020

 File size            Ratio  Format      Name
 -------------------- ------ ----------- -----------
 46242352 -> 14774288 31.95% macho/amd64 upx-compression-demo-1.0.0-SNAPSHOT-runner

...

如你所见,这次它运行了 UPX 来压缩原生可执行文件。如果你检查大小,应该会得到大约 15 MB

.rwxr-xr-x 15M clement 11 Dec 18:03 upx-compression-demo-1.0.0-SNAPSHOT-runner

所以我们从 46 MB 变成了 15 MB;这是一个显著的提高,尽管它仍然装不下软盘。

**重要** 警告

然而,正如引言中所说,天下没有免费的午餐。之前,我们也测量了未压缩可执行文件的内存使用情况(21 MB)。让我们将其与压缩后的可执行文件进行比较。

使用以下命令运行应用程序

> ./target/upx-compression-demo-1.0.0-SNAPSHOT-runner

然后在另一个终端中运行

> curl https://:8080/hello
Hello RESTEasy Reactive%

> rss runner
PID 0M COMMAND
4501 57M ./target/upx-compression-demo-1.0.0-SNAPSHOT-runner

**57 MB**!因此,它使用的 RSS 是未压缩可执行文件的约 2.7 倍。这种开销是因为压缩后的可执行文件必须在启动时解压程序并将其存储在内存中。它也可能增加启动时间,但这种启动开销在大多数情况下都可以忽略不计。

总结

UPX 允许你压缩你的原生可执行文件。在 Quarkus 2.6 中,你只需配置压缩级别,就可以了,它会为你压缩。

但是,不要认为这一切都是免费的。虽然磁盘空间节省效果显著,但不要忽视 RSS 的开销。

UPX 压缩可以使命令行工具或磁盘空间受限的环境受益。对于长时间运行的应用程序或微服务,RSS 开销会降低部署密度。因此,如果存储不是问题,或者部署密度对你至关重要,最好不要压缩你的可执行文件。