减小 go 二进制文件体积

常常有人用 go 与 c语言比较二进制的大小,而且举例 go 生成的二进制多么的大 c 生成的二进制多么的小,这点我是看不下去的。因为 go 官方的编译器生成的二进制是静态的,而 c 语言一般使用 gcc 生成 二进制,而 gcc 默认是动态的。用静态和动态比较是非常不公平的。如果实在需要比较的话,就静态对静态 动态对动态。

linux 可以使用 ldd 查看 可执行文件是否包含动态链

接下来就开始演示了。

编写一个 c 语言的 hello world 接下来会用到它 hello.c

# include <stdio.h>

int main() {
    printf("Hello World!\n");

    return 0;
}

再编写一个 go 语言的 hello World hello_fmt.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World!")
}

再用 go 内置 的 print 编写一个 hello world hello_builtin.go

package main

func main() {
    println("Hello World!") // 不建议在生产环境中使用
}

写入好后目录结构大概如下

[email protected]:~/code$ du -h *
4.0K    hello_builtin.go
4.0K    hello.c
4.0K    hello_fmt.go
[email protected]:~/code$

写接下来编译它们,为了方便区分,我们在文件名前加上标识

gcc -o c_hello hello.c

go build -o go_hello_fmt hello_fmt.go

go build -o go_hello_builtin hello_builtin.go

编译好后 大小如下

[email protected]:~/code$ gcc -o c_hello hello.c
[email protected]:~/code$ go build -o go_hello_fmt hello_fmt.go
[email protected]:~/code$ go build -o go_hello_builtin hello_builtin.go
[email protected]:~/code$ du -h *
12K     c_hello
1.1M    go_hello_builtin
1.9M    go_hello_fmt
4.0K    hello_builtin.go
4.0K    hello.c
4.0K    hello_fmt.go
[email protected]:~/code$

是不是感觉 体积差别巨大,那是因为 go默认编译器 的二进制文件默认包含调试信息,而 gcc 生成的代码默认不包含调试信息。我们可以使用 -ldflags "-s -w" 删除它

go build -ldflags "-s -w" -o delDebug_go_hello_fmt hello_fmt.go

go build -ldflags "-s -w" -o delDebug_go_hello_builtin hello_builtin.go

编译好后大小如下

[email protected]:~/code$ go build -ldflags "-s -w" -o delDebug_go_hello_fmt hello_fmt.go
[email protected]:~/code$ go build -ldflags "-s -w" -o delDebug_go_hello_builtin hello_builtin.go
[email protected]:~/code$ du -h *                             12K     c_hello
744K    delDebug_go_hello_builtin
1.3M    delDebug_go_hello_fmt
1.1M    go_hello_builtin
1.9M    go_hello_fmt
4.0K    hello_builtin.go
4.0K    hello.c
4.0K    hello_fmt.go
[email protected]:~/code$

卧槽!大小差距还是很大,别着急那是因为 go编译器默认是采用的静态键而 gcc 默认采用的是动态链,关于什么是动态键什么是静态链大家可以自行 google 一下这里就不细说了。 linux 下可以使用 ldd 命令查看 可执行文件是否是 动态链。

为了公平起见我们将 c 版的 hello world 转成静态的

gcc -static -o static_c_hello hello.c

大小如下

[email protected]:~/code$ gcc -static -o static_c_hello hello.c
[email protected]:~/code$ du -h *
12K     c_hello
744K    delDebug_go_hello_builtin
1.3M    delDebug_go_hello_fmt
1.1M    go_hello_builtin
1.9M    go_hello_fmt
4.0K    hello_builtin.go
4.0K    hello.c
4.0K    hello_fmt.go
792K    static_c_hello
[email protected]:~/code$

这下是不是就感觉差不多了,甚至 go 使用了内置关键字生成的代码还要小一点 (逃

为了动态比动态静态比静态我们将 go 的代码也使用 动态链的方式进行编译。

关注 go 语言代码的话动态链方式生成最简单的是使用 gccgo 当然go 默认编译器 gc 也支持动态链方式编译。这里为了简单演示就使用 gccgo 了。如果你编写的代码使用了新的语法的话建议使用 gc 而不是 gccgo 毕竟gc 是 go 语言官方维护的编译器关于 gccgo 请查看 https://golang.org/doc/install/gccgo

gccgo 请自行安装毕竟各个 linux 发行版关于 gccgo 的包名有一点微小的不同,不建议编译安装,建议使用发行版自带的包管理器进行安装。

debian 系安装 gccgo

sudo apt install gccgo

arch 安装 gccgo

sudo pacman -S gcc-go

其他发行版就不演示了,一般来说用包管理器搜索一下就出来了

gccgo 安装好后开始生成 二进制

gccgo -o gccgo_hello_fmt hello_fmt.go

gccgo -o gccgo_hello_builtin hello_builtin.go

以下是生成结果

[email protected]:~/code$ gccgo -o gccgo_hello_fmt hello_fmt.go
[email protected]:~/code$ gccgo -o gccgo_hello_builtin hello_builtin.go
[email protected]:~/code$ du -h *
12K     c_hello
744K    delDebug_go_hello_builtin
1.3M    delDebug_go_hello_fmt
28K     gccgo_hello_builtin
32K     gccgo_hello_fmt
1.1M    go_hello_builtin
1.9M    go_hello_fmt
4.0K    hello_builtin.go
4.0K    hello.c
4.0K    hello_fmt.go
792K    static_c_hello
[email protected]:~/code$

是不是感觉小多了虽然还是比 c 大一倍左右不过也没有之前夸张了。

当然 gccgo 也可以生成静态链的代码这里我稍微演示一下

gccgo -static -o static_gccgo_hello_fmt hello_fmt.go

gccgo -static -o static_gccgo_hello_builtin hello_builtin.go

以下是输出的结果

[email protected]:~/code$ gccgo -static -o static_gccgo_hello_fmt hello_fmt.go
[email protected]:~/code$ gccgo -static -o static_gccgo_hello_builtin hello_builtin.go
[email protected]:~/code$ du -h *
12K     c_hello
744K    delDebug_go_hello_builtin
1.3M    delDebug_go_hello_fmt
28K     gccgo_hello_builtin
32K     gccgo_hello_fmt
1.1M    go_hello_builtin
1.9M    go_hello_fmt
4.0K    hello_builtin.go
4.0K    hello.c
4.0K    hello_fmt.go
792K    static_c_hello
1.8M    static_gccgo_hello_builtin
4.7M    static_gccgo_hello_fmt
[email protected]:~/code$

这里生成静态链的 go 代码 相比较之下 gc 生成的代码要小一点

动态链和静态链,一般来说我比较倾向于静态链,因为静态链的移植性比较好 而动态链比较省空间,相同的库只存在一份,大家共同使用,关于 go 的二进制压缩可以参考我的另外一篇文章 upx 教程

0%