Reason for huge size of compiled executable of Go (3个答案)
去年关闭。
编辑/澄清:看来我在这里没有解释自己。我不是在批评Go,不是运行时,也不是可执行文件很大的事实。我也不是想说C是好的而Go是不好的。
我只是指出,编译后的可执行文件似乎总是至少在1MB左右(大概是运行时的开销),并且导入软件包似乎会将整个软件包放入其中,而不管其用法如何。
我的实际问题基本上是这两个点是默认行为还是唯一行为?我给出了一些C语言程序的示例,这些示例在代码方面等效于Go程序,但是我为它们精心挑选了编译器和链接器标志,以避免与任何外部C运行时进行链接(我确实使用
Dependency Walker进行了验证)。 C示例的目的是演示实际代码有多小,并演示您确实需要某些东西而仅导入所需内容的情况。
实际上,我认为这种行为(以防万一)是默认情况下的一个好设置,但是我认为可能会有一些编译器或链接器标志来更改此设置。现在,我不认为您不希望运行时或运行时的一部分是明智的。但是,我认为有选择地包括包装的各个部分并不是一件奇怪的事情。让我解释:
假设我们正在用C / C++编写,并且包含了一个包含大量功能的巨大头文件,但是我们仅使用其中的一小部分。在这种情况下,可能最终得到一个可执行文件,该可执行文件将不包含该头文件中的任何未使用的代码。 (真实示例:一个数学库,支持2D / 3D / 4D向量和矩阵,四元数等。所有这些库都有2个版本,一个用于32位浮点数,一个用于64位浮点数+从一个到另一个的转换)
这是我一直在寻找的东西。我完全理解这样做可能在某些情况下会引起问题,但仍然如此。并不是说Go没有其他可能导致严重问题的事物。.他们拥有“不安全”软件包,如果需要的话,可以使用该软件包,但是就像“自担风险”软件包一样。
原始问题:在使用Go(golang)一段时间后,我决定研究它生成的可执行文件。我看到我的项目仅针对可执行文件的时钟就超过4.5MB,而另一个在复杂度和范围上相似但使用C / C++(与MSVC编译)一起编写的项目则不到100KB。
所以我决定尝试一些事情。我已经用C和Go编写了非常愚蠢和无用的简单程序来比较输出。
对于C,我正在使用MSVC,以发布模式编译64位可执行文件,并且未与C运行时链接(据我所知,Go可执行文件仅在使用CGO时才与其链接)
第一次运行:一个简单的无穷循环,就是这样。没有打印,没有与操作系统的交互,什么也没有。
C:
#include "windows.h"
int main()
{
while (true);
}
void mainCRTStartup()
{
main();
}
走:
package main
func main() {
for {
}
}
结果:C:3KB可执行文件。一无所有
GO:1,057 KB可执行文件。取决于KERNEL32.DLL的29个过程
那里有很大的区别,但是我认为这可能是不公平的。因此,下一个测试我决定删除循环,只是编写一个程序,该程序立即以退出代码13返回:
C:
#include "windows.h"
int main()
{
return 13;
}
void mainCRTStartup()
{
ExitProcess(main());
}
走:
package main
import "os"
func main() {
os.Exit(13)
}
结果:C:4KB可执行文件。取决于KERNEL32.DLL中的1个过程
GO:1,281 KB可执行文件。取决于KERNEL32.DLL中的31个过程
看来Go可执行文件“blo肿”。我了解到,与C不同,Go将大量的运行时代码放入可执行文件中,这是可以理解的,但不足以解释其大小。
同样,Go看起来像是在软件包粒度上工作。我的意思是,它不会塞入您不使用的可执行程序包中,但是,即使您只需要一个很小的子集,即使您根本不使用它,也可以导入所有包。 。例如,仅导入“fmt”而未在其中调用任何内容,会将先前的可执行文件从1,281KB扩展到1,777KB。
我是否在Go编译器中丢失了一些标志,以使其显得不那么(肿(我知道我可以设置许多标志,还可以将标志提供给本机编译器和链接器,但我没有为此专门找到)难道这就是2019年没人关心的事情了,因为实际上只有几兆字节?
以下是Go程序包括的一些C程序不包括的东西:
容器类型,例如哈希映射和数组,以及它们的关联函数内存分配器,对多线程程序进行了优化并发垃圾收集器线程的类型和函数,例如互斥锁,条件变量,通道和线程调试工具,例如堆栈跟踪转储和SIGQUIT处理程序反射码(如果您确切地知道其中包含的内容,则可以使用调试工具查看二进制文件中的符号。在macOS和Linux上,可以使用
nm
将符号转储到程序中。)
问题是,大多数Go程序都使用所有这些功能!很难想象不使用垃圾回收器的Go程序。因此,Go的创建者尚未创建从程序中删除此代码的特殊方法-因为没人需要此功能。毕竟,您真的关心
"Hello, world!"
有多大吗?
不,您不需要。 从常见问题解答
Why is my trivial program such a large binary? 默认情况下,gc工具链中的链接器会创建静态链接的二进制文件。因此,所有Go二进制文件都包含Go运行时,以及支持动态类型检查,反射甚至紧急时间堆栈跟踪所必需的运行时类型信息。
还请记住,如果要在Windows上使用MSVC进行编译,则可能正在使用DLL运行时,例如MSVCR120.DLL ...,大约1 MB。