节约空间,能省一点是一点

go build使用的是静态编译,会将程序的依赖一起打包,这样一来编译得到的可执行文件可以直接在目标平台运行,无需运行环境(例如 JRE)或动态链接库(例如 DLL)的支持。

虽然 Go 的静态编译很方便,但也存在一个问题:打包生成的可执行文件体积较大,毕竟相关的依赖都被打包进来了。今天就来尝试一下压缩 Go 编译得到的可执行文件的体积。

1. 添加 -ldflags 参数

在程序编译的时候可以加上-ldflags "-s -w"参数来优化编译,原理是通过去除部分链接和调试等信息来减小编译生成的可执行程序体积,具体参数如下:

  • -a:强制编译所有依赖包
  • -s去掉符号表信息,不过panic的时候stace trace没有任何文件名/行号信息
  • -w去掉DWARF调试信息,不过得到的程序就不能使用gdb进行调试

:不建议-w-s同时使用

2. 编译优化示例

未添加编译参数main.exe原体积约为19.6M

> go build main.go # 直接编译

> ls -al main.exe # 约为 19.6M
-rwxr-xr-x 1 abelsu7 197609 20556800 10月 25 15:52 main.exe*

添加-w参数,去掉调试信息,体积减小至约15.8M

> go build -ldflags "-w" main.go # 添加 -w,去掉调试信息

> ls -al main.exe # 约为 15.8M
-rwxr-xr-x 1 abelsu7 197609 16569344 10月 25 15:54 main.exe*

添加-s参数,去掉符号表,体积减小至约14.7M

> go build -ldflags "-s" main.go # 添加 -s,去掉符号表

> ls -al main.exe # 约为 14.7M
-rwxr-xr-x 1 abelsu7 197609 15397888 10月 25 15:59 main.exe*

同时添加-w -s参数,体积同样约为14.7M

> go build -ldflags "-w -s" main.go # 同时添加 -w -s

> ls -al main.exe # 同样约为 14.7M
-rwxr-xr-x 1 abelsu7 197609 15397888 10月 25 16:04 main.exe*
  • 可以看到添加-s参数时可执行文件体积减小最多
  • 若对符号表无需求,-ldflags直接添加"-s"即可

3. 使用 upx

UPX - the Ultimate Packer for eXecutables 是一款开源的可执行文件压缩程序,可以压缩常见平台下的可执行程序包。

3.1 安装 upx

Releases 页面下载对应平台的upx,MacOS 和 Linux 可以直接安装发行版:

# For MacOS
> brew install upx

# For CentOS/Fedora/RHEL
> yum install upx

> yum info upx
Installed Packages
Name        : upx
Arch        : x86_64
Version     : 3.95
Release     : 4.el7
Size        : 1.8 M
Repo        : installed
From repo   : epel
Summary     : Ultimate Packer for eXecutables
URL         : http://upx.sourceforge.net/
License     : GPLv2+ and Public Domain
Description : UPX is a free, portable, extendable, high-performance executable
            : packer for several different executable formats. It achieves an
            : excellent compression ratio and offers very fast decompression. Your
            : executables suffer no memory overhead or other drawbacks.

3.2 直接编译后压缩

直接go build后压缩,main.exe体积从19.6M压缩至9.1M,为原体积的46.48%

> go build main.go

> ls -al main.exe # 约为 19.6M
-rwxr-xr-x 1 abelsu7 197609 20556800 10月 25 16:32 main.exe*


> upx main.exe # 压缩至原体积的 46.48%
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2018
UPX 3.95w       Markus Oberhumer, Laszlo Molnar & John Reiser   Aug 26th 2018

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
  20556800 ->   9554944   46.48%    win64/pe     main.exe

Packed 1 file.

> ls -al main.exe # 约为 9.1M
-rwxr-xr-x 1 abelsu7 197609 9554944 10月 25 16:32 main.exe*

3.3 编译优化后压缩

添加-ldflags "-s"后压缩,main.exe体积从14.7M压缩至4.8M,为原体积的32.42%

> go build -ldflags "-s" main.go

> ls -al main.exe # 约为 14.7M
-rwxr-xr-x 1 abelsu7 197609 15397888 10月 25 16:38 main.exe*

> upx main.exe # 压缩至原体积的 32.42%
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2018
UPX 3.95w       Markus Oberhumer, Laszlo Molnar & John Reiser   Aug 26th 2018

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
  15397888 ->   4992512   32.42%    win64/pe     main.exe

Packed 1 file.

> ls -al main.exe # 约为 4.8M
-rwxr-xr-x 1 abelsu7 197609 4992512 10月 25 16:38 main.exe*

最终经过-ldflags编译优化upx压缩main.exe体积从19.6M压缩至4.8M约为原体积的24.5%,效果还是很明显的

3.4 交叉编译后压缩

另外,upx不仅可以压缩当前主机平台的可执行程序,只要是支持的平台都可以进行压缩。因此可以先在本机进行交叉编译,之后使用上述方法压缩可执行程序,最后再将可执行程序传至目标平台运行

关于交叉编译,可以参考上一篇文章:Go 程序的交叉编译、选择性编译 | 苏易北

以在windows/amd64下交叉编译linux/amd64为例,main体积从19.9M压缩至4.8M,约为原体积的24.2%,压缩比例大致相同:

# 设置交叉编译环境变量
> set GOOS=Linux
> set GOARCH=amd64
> set CGO_ENABLED=0

# 直接交叉编译
> go build main.go

> ls -al main # 约为 19.9M
-rw-r--r-- 1 abelsu7 197609 20837787 10月 25 16:51 main

# 开启编译优化,去掉符号表
> go build -ldflags "-s" main.go

> ls -al main # 约为 14.7M
-rw-r--r-- 1 abelsu7 197609 15464736 10月 25 16:51 main

> upx main # 压缩至原体积的 32.60%
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2018
UPX 3.95w       Markus Oberhumer, Laszlo Molnar & John Reiser   Aug 26th 2018

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
  15464736 ->   5041696   32.60%   linux/amd64   main

Packed 1 file.

> ls -al main # 约为 4.8M
-rw-r--r-- 1 abelsu7 197609 5041696 10月 25 16:51 main

3.5 upx 的压缩选项

下面是upx的一些常用参数

  • -o:指定输出的文件名
  • -k:保留备份原文件
  • -1最快压缩,共1-9九个级别
  • -9最优压缩,与上面对应
  • -d解压缩decompress,恢复原体积
  • -l显示压缩文件的详情,例如upx -l main.exe
  • -t测试压缩文件,例如upx -t main.exe
  • -q静默压缩be quiet
  • -v显示压缩细节be verbose
  • -f强制压缩
  • -V:显示版本号
  • -h:显示帮助信息
  • --brute:尝试所有可用的压缩方法slow
  • --ultra-brute:比楼上更极端,very slow

参考文章

  1. Go 编译生成更小的执行程序 | 一线攻城狮
  2. 压缩 go build 打包的可执行文件:3.4MB->897K | 简书
  3. upx - the Ultimate Packer for eXecutables | Github