我编写了一个hello world Go程序,该程序在linux机器上生成了本机可执行文件。 但是我很惊讶地看到简单的Hello world Go程序的大小为1.9MB!

为什么Go中如此简单的程序的可执行文件如此庞大?

确切的问题出现在官方常见问题解答中:为什么我的琐碎程序这么大的二进制文件?

引用答案:

The linkers in the gc tool chain (5l, 6l, and 8l) do static linking. All Go binaries therefore include the Go run-time, along with the run-time type information necessary to support dynamic type checks, reflection, and even panic-time stack traces.

A simple C"hello, world" program compiled and linked statically using gcc on Linux is around 750 kB, including an implementation of printf. An equivalent Go program using fmt.Printf is around 1.9 MB, but that includes more powerful run-time support and type information.

因此,Hello World的本机可执行文件为1.9 MB,因为它包含一个运行时,该运行时提供了垃圾回收,反射和许多其他功能(您的程序可能并未真正使用,但确实存在)。以及用于打印"Hello World"文本(及其依赖项)的fmt包的实现。

现在尝试以下操作:在您的程序中添加另一条fmt.Println("Hello World! Again")行,然后再次对其进行编译。结果将不是2x 1.9MB,而是1.9 MB!是的,因为所有使用的库(fmt及其依赖项)和运行时已添加到可执行文件中(因此,仅会再添加几个字节来打印刚添加的第二个文本)。

考虑以下程序:

如果我在Linux AMD64机器(Go 1.9)上构建此文件,则如下所示:

我得到的二进制文件大小约为2 Mb。

这样做的原因(已在其他答案中进行了解释)是因为我们使用的" fmt"包很大,但是二进制文件也没有被剥离,这意味着符号表仍然存在。如果我们改为指示编译器剥离二进制文件,它将变得更小:

但是,如果我们重写程序以使用内置函数print而不是fmt.Println,如下所示:

然后编译它:

我们最终得到了一个甚至更小的二进制文件。这是我们能够得到的最小尺寸,而无需借助UPX打包之类的技巧,因此Go运行时的开销大约为700 Kb。

请注意,golang / go项目中的问题6853跟踪了二进制大小问题。

例如,提交a26c01a(对于Go 1.4)将hello world减少70kB:

because we don't write those names into the symbol table.

考虑到1.5的编译器,汇编器,链接器和运行时将是
完全在Go中,您可以期待进一步的优化。

Update 2016 Go 1.7:已优化:请参阅" Smaller Go 1.7二进制文件"。

但是这些天(2019年4月)占据最大的位置是runtime.pclntab
请参阅Raphael'kena'Poss的"为什么我的Go可执行文件这么大?使用D3大小的Go可执行文件可视化"。

It is not too well documented however this comment from the Go source code suggests its purpose:

1
// A LineTable is a data structure mapping program counters to line numbers.

The purpose of this data structure is to enable the Go runtime system to produce descriptive stack traces upon a crash or upon internal requests via the runtime.GetStack API.

So it seems useful. But why is it so large?

The URL https://golang.org/s/go12symtab hidden in the aforelinked source file redirects to a document that explains what happened between Go 1.0 and 1.2. To paraphrase:

prior to 1.2, the Go linker was emitting a compressed line table, and the program would decompress it upon initialization at run-time.

in Go 1.2, a decision was made to pre-expand the line table in the executable file into its final format suitable for direct use at run-time, without an additional decompression step.

In other words, the Go team decided to make executable files larger to save up on initialization time.

Also, looking at the data structure, it appears that its overall size in compiled binaries is super-linear in the number of functions in the program, in addition to how large each function is.

https://science.raphael.poss.name/go-executable-size-visualization-with-d3/size-demo-ss.png