我编译了一个 hello world Go 程序,它在我的 linux 机器上生成了本机可执行文件。但是我很惊讶地看到简单的 Hello world Go 程序的大小,它是 1.9MB !
为什么这么简单的Go程序的可执行文件这么大?
引用答案:
在GC工具链(连接体5l,6l和8l)做静态链接。因此,所有 Go 二进制文件都包含 Go 运行时,以及支持动态类型检查、反射甚至恐慌时堆栈跟踪所需的运行时类型信息。 在 Linux 上使用 gcc 静态编译和链接的简单 C“hello, world”程序大约 750 kB,包括printf. 使用的等效 Go 程序fmt.Printf大约为 1.9 MB,但这包括更强大的运行时支持和类型信息。
在GC工具链(连接体5l,6l和8l)做静态链接。因此,所有 Go 二进制文件都包含 Go 运行时,以及支持动态类型检查、反射甚至恐慌时堆栈跟踪所需的运行时类型信息。
5l
6l
8l
在 Linux 上使用 gcc 静态编译和链接的简单 C“hello, world”程序大约 750 kB,包括printf. 使用的等效 Go 程序fmt.Printf大约为 1.9 MB,但这包括更强大的运行时支持和类型信息。
printf
fmt.Printf
因此,您的 Hello World 的本机可执行文件是 1.9 MB,因为它包含一个运行时,该运行时提供垃圾收集、反射和许多其他功能(您的程序可能不会真正使用这些功能,但它就在那里)。以及fmt用于打印"Hello World"文本的包的实现(及其依赖项)。
fmt
"Hello World"
现在尝试以下操作:fmt.Println("Hello World! Again")向您的程序添加另一行并再次编译它。结果不会是 2x 1.9MB,但仍然只有 1.9MB!是的,因为所有使用的库(fmt及其依赖项)和运行时都已添加到可执行文件中(因此只会添加几个字节来打印您刚刚添加的第二个文本)。
fmt.Println("Hello World! Again")
考虑以下程序:
package main import "fmt" func main() { fmt.Println("Hello World!") }
如果我在我的 Linux AMD64 机器(Go 1.9)上构建它,像这样:
$ go build $ ls -la helloworld -rwxr-xr-x 1 janf group 2029206 Sep 11 16:58 helloworld
我得到一个大小约为 2 Mb 的二进制文件。
这样做的原因(已在其他答案中解释过)是我们使用的“fmt”包非常大,但二进制文件也没有被剥离,这意味着符号表仍然存在。如果我们改为指示编译器剥离二进制文件,它会变得更小:
$ go build -ldflags "-s -w" $ ls -la helloworld -rwxr-xr-x 1 janf group 1323616 Sep 11 17:01 helloworld
但是,如果我们重写程序使用内置函数print,而不是fmt.Println,像这样:
package main func main() { print("Hello World!\n") }
然后编译它:
$ go build -ldflags "-s -w" $ ls -la helloworld -rwxr-xr-x 1 janf group 714176 Sep 11 17:06 helloworld
我们最终得到一个更小的二进制文件。这是我们无需借助 UPX 打包之类的技巧即可获得的尽可能小,因此 Go-runtime 的开销大约为 700 Kb。