Golang | 靜態編譯

**問題:**一樣一個Hello World程序,爲何go的編譯出來後,大小比C編譯的大的多linux

由於C採用的是動態連接庫形式編譯,而go採用的是靜態編譯的形式,致使了編譯後的二進制文件體積較大;windows

早期的計算機內存資源十分寶貴,才引入了動態裝入的思想,也就產生了動態連接庫;函數

如今的計算機內存動輒幾十G,徹底不用爲了節省幾百k或者1M內存而大費周章;ui

go採用靜態編譯,不依賴任何動態連接庫,能夠避免各類動態連接庫依賴的問題;spa

編譯好後,只要平臺是一致的(linux amd64),就能夠任意部署,徹底不用考慮連接庫依賴問題;插件

這是優勢啊,居然有人吐槽編譯後可執行文件體積太大?線程

動態連接庫

在使用動態連接庫的時候並不會把動態庫的代碼所有copy到生成的程序中,而是在程序運行的時候再去加載對應的代碼,因此使用同一個動態庫的程序能夠共用一份代碼,並且當你對動態庫進行升級的時候,也不用去修改使用它的代碼。code

同時,解決了相同的機器碼在物理內存上存儲多份的問題;進程

在物理內存上存放一份,哪一個進程須要能夠將這個代碼映射到本身的虛擬地址空間上;內存

並且隨時能夠升級動態連接庫的內容,而不須要將已經運行的程序停下來;

也就是程序運行的時候本身佔用必定的內存,加載動態連接庫的時候將他載入到內存中,這兩塊內存沒有半毛錢關係。只不過是程序將動態連接庫的地址映射到本身的內存空間,以便調用罷了;

動態連接庫的加載
  • 隱式連接:須要.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下還無法使用。

相關文章
相關標籤/搜索