**问题:**同样一个Hello World程序,为什么go的编译出来后,大小比C编译的大的多

因为C采用的是动态链接库形式编译,而go采用的是静态编译的形式,导致了编译后的二进制文件体积较大;

早期的计算机内存资源十分宝贵,才引入了动态装入的思想,也就产生了动态链接库;

现在的计算机内存动辄几十G,完全不用为了节省几百k或者1M内存而大费周章;

go采用静态编译,不依赖任何动态链接库,可以避免各种动态链接库依赖的问题;

编译好后,只要平台是一致的(linux amd64),就可以任意部署,完全不用考虑链接库依赖问题;

这是优点啊,竟然有人吐槽编译后可执行文件体积太大?

动态链接库

在使用动态链接库的时候并不会把动态库的代码全部copy到生成的程序中,而是在程序运行的时候再去加载对应的代码,所以使用同一个动态库的程序可以共用一份代码,而且当你对动态库进行升级的时候,也不用去修改使用它的代码。

同时,解决了相同的机器码在物理内存上存储多份的问题;

在物理内存上存放一份,哪个进程需要可以将这个代码映射到自己的虚拟地址空间上;

而且随时可以升级动态链接库的内容,而不需要将已经运行的程序停下来;

也就是程序运行的时候自己占用一定的内存,加载动态链接库的时候将他载入到内存中,这两块内存没有半毛钱关系。只不过是程序将动态链接库的地址映射到自己的内存空间,以便调用罢了;

动态链接库的加载
  • 隐式链接:需要.lib .h .dll,程序开始执行时就将文件加载到内存中; #pragma comment(lib, "Dll.lib")
  • 显式链接:需要.h .dll,实时加载,程序需要的时候才加载;
  • 如此看来go的plugin是隐式链接的;
如何升级运行中的dll

开一个线程检测dll,当发现有新版本时,先卸载旧的dll,然后加载新的;

go又是如何升级插件的呢?

p, err := plugin.Open("plugin1.so")
p, err := plugin.Open("plugin2.so")
定义一个全局变量,第二次open的时候覆盖第一次open就行了,下次调用就是新的函数了
当第二个so加载后,前面so的数据是没办法访问到,也就是说数据没法热更新(这时插件就不能用全局变量了)
//第一个插件不是还占着内存吗?等着被GC呗

//go的plugin只有打开模块和提取符号,没有关闭(卸载)
同一个so文件只能被打开一次
热更新只更新函数,不更新数据

//编译
go build -ldflags "-pluginpath=plugin1" -buildmode=plugin -o plugin1.so *.go
--ldflags里的-pluginpath的作用是: 每次编译的内部识别路径都是不同的, 避免重复加载的警告
复制代码

linux下的动态链接库是.so,静态库是.a

windows下动态链接库是.dll

需要注意的是,目前go的动态链接库(plugin)只支持linux,windows下还没法使用。