事情是這樣的,因?yàn)樾●R本次要寫一個(gè)go項(xiàng)目。但是因?yàn)橐恍?quán)限問題,一些依賴包在內(nèi)網(wǎng)小馬獲取不到,于是只能求助大大。大大給的策略就是他先把所有的依賴包go mod,然后go mod vendor遷移到項(xiàng)目目錄vendor下進(jìn)行本地依賴載入即可,也就是使用 go build -mod=vendor來編譯即可。一切似乎看起來還是那么完美。然后正要起飛,直接翻車,現(xiàn)場(chǎng)如下?!具@里插播一條發(fā)現(xiàn),就是使用golang IDE go build 和使用命令行g(shù)o build 的區(qū)別在于前者不會(huì)生成.exe文件】
成都創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司是一家服務(wù)多年做網(wǎng)站建設(shè)策劃設(shè)計(jì)制作的公司,為廣大用戶提供了做網(wǎng)站、網(wǎng)站設(shè)計(jì),成都網(wǎng)站設(shè)計(jì),廣告投放平臺(tái),成都做網(wǎng)站選成都創(chuàng)新互聯(lián),貼合企業(yè)需求,高性價(jià)比,滿足客戶不同層次的需求一站式服務(wù)歡迎致電。
將大大go mod vendor完的包pull到本地,只要編譯就會(huì)發(fā)生如下錯(cuò)誤(以下省略了一部分類似的報(bào)錯(cuò))。其實(shí)是 go.mod內(nèi)的所有依賴包都報(bào)錯(cuò)。
大大說他的本地編譯是正常的。不得不懷疑是不是因?yàn)榇蟠蟊镜豨opath還有一份包依賴的原因,然而經(jīng)查并不是這個(gè)問題。翻閱了網(wǎng)絡(luò)上的大部分資料無果,網(wǎng)絡(luò)上要么是說是因?yàn)樽R(shí)別不到包,按照提示重新go?mod?vendor一下就可以了。小馬蠻試了一下,不出所料必然地報(bào)遠(yuǎn)程報(bào)獲取不到呢,IDE的報(bào)錯(cuò)定位其實(shí)是不準(zhǔn)確的。再次檢查vendor/modules.txt文件,沒有問題,無果。 于是開始質(zhì)疑golang IDE 的版本支持問題,無果。看了下go.mod文件中寫著go 1.14,也沒錯(cuò)呢,小馬用的GO SDK正是1.14.4版本。敲出go env 查看環(huán)境配置,GO111MODULE=on,因?yàn)榄h(huán)境變量是auto,但是go到一定版本后默認(rèn)是on,也沒問題,無果。那問題出在哪呢?由于沒有依賴包拉取權(quán)限,只能再次求助大大,大大表示也很奇怪,一番折騰,于是問題得到解決?!具@里插播一條好玩的東西,就是GO111MODULE為什么是GO111呢,因?yàn)槠鋵?shí)1.11版本開始支持MODULE的】
結(jié)論是:因?yàn)榇蟠骻o? mod的時(shí)候用的是go 1.13,而我編譯的時(shí)候用的 1.14,所以就報(bào)了這個(gè)奇怪的錯(cuò)誤。you what?直接懵逼。但是為啥go.mod文件中寫的版本要求是1.14,而大大用1.13也編譯得好好的。
這是個(gè)大坑,掉進(jìn)坑里自己撲騰了一天!!希望大家謹(jǐn)慎入坑。
爬坑一小時(shí)出坑一秒鐘,每一次的爬坑都是充滿著十八般絕技。奇怪的姿勢(shì)又增加了。
go運(yùn)行方式有哪幾種?
如果GO111MODULE是auto則根據(jù)項(xiàng)目目錄位置和是否含有g(shù)o.mod文件來決定使用什么模式。如果是GO111MODULE=off則使用gopath,如果是on則使用module模式。gopath模式下的src目錄下不能有g(shù)o.mod文件,否則報(bào)錯(cuò)。
一些go mod命令 記錄備用,國內(nèi)的資料并不多(注意go mod?命令在?$GOPATH?里默認(rèn)是執(zhí)行不了的,因?yàn)?GO111MODULE?的默認(rèn)值是?auto。默認(rèn)在$GOPATH?里是不會(huì)執(zhí)行, 如果一定要強(qiáng)制執(zhí)行,就設(shè)置環(huán)境變量為?on。):
Go 語言較之 C 語言一個(gè)很大的優(yōu)勢(shì)就是自帶 GC 功能,可 GC 并不是沒有代價(jià)的。寫 C 語言的時(shí)候,在一個(gè)函數(shù)內(nèi)聲明的變量,在函數(shù)退出后會(huì)自動(dòng)釋放掉,因?yàn)檫@些變量分配在棧上。如果你期望變量的數(shù)據(jù)可以在函數(shù)退出后仍然能被訪問,就需要調(diào)用 malloc 方法在堆上申請(qǐng)內(nèi)存,如果程序不再需要這塊內(nèi)存了,再調(diào)用 free 方法釋放掉。Go 語言不需要你主動(dòng)調(diào)用 malloc 來分配堆空間,編譯器會(huì)自動(dòng)分析,找出需要 malloc 的變量,使用堆內(nèi)存。編譯器的這個(gè)分析過程就叫做逃逸分析。
所以你在一個(gè)函數(shù)中通過 dict := make(map[string]int) 創(chuàng)建一個(gè) map 變量,其背后的數(shù)據(jù)是放在??臻g上還是堆空間上,是不一定的。這要看編譯器分析的結(jié)果。
可逃逸分析并不是百分百準(zhǔn)確的,它有缺陷。有的時(shí)候你會(huì)發(fā)現(xiàn)有些變量其實(shí)在棧空間上分配完全沒問題的,但編譯后程序還是把這些數(shù)據(jù)放在了堆上。如果你了解 Go 語言編譯器逃逸分析的機(jī)制,在寫代碼的時(shí)候就可以有意識(shí)地繞開這些缺陷,使你的程序更高效。
Go 語言雖然在內(nèi)存管理方面降低了編程門檻,即使你不了解堆棧也能正常開發(fā),但如果你要在性能上較真的話,還是要掌握這些基礎(chǔ)知識(shí)。
這里不對(duì)堆內(nèi)存和棧內(nèi)存的區(qū)別做太多闡述。簡(jiǎn)單來說就是, 棧分配廉價(jià),堆分配昂貴。 棧空間會(huì)隨著一個(gè)函數(shù)的結(jié)束自動(dòng)釋放,堆空間需要時(shí)間 GC 模塊不斷地跟蹤掃描回收。如果對(duì)這兩個(gè)概念有些迷糊,建議閱讀下面 2 個(gè)文章:
這里舉一個(gè)小例子,來對(duì)比下堆棧的差別:
stack 函數(shù)中的變量 i 在函數(shù)退出會(huì)自動(dòng)釋放;而 heap 函數(shù)返回的是對(duì)變量 i 的引用,也就是說 heap() 退出后,表示變量 i 還要能被訪問,它會(huì)自動(dòng)被分配到堆空間上。
他們編譯出來的代碼如下:
邏輯的復(fù)雜度不言而喻,從上面的匯編中可看到, heap() 函數(shù)調(diào)用了 runtime.newobject() 方法,它會(huì)調(diào)用 mallocgc 方法從 mcache 上申請(qǐng)內(nèi)存,申請(qǐng)的內(nèi)部邏輯前面文章已經(jīng)講述過。堆內(nèi)存分配不僅分配上邏輯比??臻g分配復(fù)雜,它最致命的是會(huì)帶來很大的管理成本,Go 語言要消耗很多的計(jì)算資源對(duì)其進(jìn)行標(biāo)記回收(也就是 GC 成本)。
Go 編輯器會(huì)自動(dòng)幫我們找出需要進(jìn)行動(dòng)態(tài)分配的變量,它是在編譯時(shí)追蹤一個(gè)變量的生命周期,如果能確認(rèn)一個(gè)數(shù)據(jù)只在函數(shù)空間內(nèi)訪問,不會(huì)被外部使用,則使用??臻g,否則就要使用堆空間。
我們?cè)? go build 編譯代碼時(shí),可使用 -gcflags '-m' 參數(shù)來查看逃逸分析日志。
以上面的兩個(gè)函數(shù)為例,編譯的日志輸出是:
日志中的 i escapes to heap 表示該變量數(shù)據(jù)逃逸到了堆上。
需要使用堆空間,所以逃逸,這沒什么可爭(zhēng)議的。但編譯器有時(shí)會(huì)將 不需要 使用堆空間的變量,也逃逸掉。這里是容易出現(xiàn)性能問題的大坑。網(wǎng)上有很多相關(guān)文章,列舉了一些導(dǎo)致逃逸情況,其實(shí)總結(jié)起來就一句話:
多級(jí)間接賦值容易導(dǎo)致逃逸 。
這里的多級(jí)間接指的是,對(duì)某個(gè)引用類對(duì)象中的引用類成員進(jìn)行賦值。Go 語言中的引用類數(shù)據(jù)類型有 func , interface , slice , map , chan , *Type(指針) 。
記住公式 Data.Field = Value ,如果 Data , Field 都是引用類的數(shù)據(jù)類型,則會(huì)導(dǎo)致 Value 逃逸。這里的等號(hào) = 不單單只賦值,也表示參數(shù)傳遞。
根據(jù)公式,我們假設(shè)一個(gè)變量 data 是以下幾種類型,相應(yīng)的可以得出結(jié)論:
下面給出一些實(shí)際的例子:
如果變量值是一個(gè)函數(shù),函數(shù)的參數(shù)又是引用類型,則傳遞給它的參數(shù)都會(huì)逃逸。
上例中 te 的類型是 func(*int) ,屬于引用類型,參數(shù) *int 也是引用類型,則調(diào)用 te(j) 形成了為 te 的參數(shù)(成員) *int 賦值的現(xiàn)象,即 te.i = j 會(huì)導(dǎo)致逃逸。代碼中其他幾種調(diào)用都沒有形成 多級(jí)間接賦值 情況。
同理,如果函數(shù)的參數(shù)類型是 slice , map 或 interface{} 都會(huì)導(dǎo)致參數(shù)逃逸。
匿名函數(shù)的調(diào)用也是一樣的,它本質(zhì)上也是一個(gè)函數(shù)變量。有興趣的可以自己測(cè)試一下。
只要使用了 Interface 類型(不是 interafce{} ),那么賦值給它的變量一定會(huì)逃逸。因?yàn)? interfaceVariable.Method() 先是間接的定位到它的實(shí)際值,再調(diào)用實(shí)際值的同名方法,執(zhí)行時(shí)實(shí)際值作為參數(shù)傳遞給方法。相當(dāng)于 interfaceVariable.Method.this = realValue
向 channel 中發(fā)送數(shù)據(jù),本質(zhì)上就是為 channel 內(nèi)部的成員賦值,就像給一個(gè) slice 中的某一項(xiàng)賦值一樣。所以 chan *Type , chan map[Type]Type , chan []Type , chan interface{} 類型都會(huì)導(dǎo)致發(fā)送到 channel 中的數(shù)據(jù)逃逸。
這本來也是情理之中的,發(fā)送給 channel 的數(shù)據(jù)是要與其他函數(shù)分享的,為了保證發(fā)送過去的指針依然可用,只能使用堆分配。
可變參數(shù)如 func(arg ...string) 實(shí)際與 func(arg []string) 是一樣的,會(huì)增加一層訪問路徑。這也是 fmt.Sprintf 總是會(huì)使參數(shù)逃逸的原因。
例子非常多,這里不能一一列舉,我們只需要記住分析方法就好,即,2 級(jí)或更多級(jí)的訪問賦值會(huì) 容易 導(dǎo)致數(shù)據(jù)逃逸。這里加上 容易 二字是因?yàn)殡S著語言的發(fā)展,相信這些問題會(huì)被慢慢解決,但現(xiàn)階段,這個(gè)可以作為我們分析逃逸現(xiàn)象的依據(jù)。
下面代碼中包含 2 種很常規(guī)的寫法,但他們卻有著很大的性能差距,建議自己想下為什么。
Benchmark 和 pprof 給出的結(jié)果:
熟悉堆棧概念可以讓我們更容易看透 Go 程序的性能問題,并進(jìn)行優(yōu)化。
多級(jí)間接賦值會(huì)導(dǎo)致 Go 編譯器出現(xiàn)不必要的逃逸,在一些情況下可能我們只需要修改一下數(shù)據(jù)結(jié)構(gòu)就會(huì)使性能有大幅提升。這也是很多人不推薦在 Go 中使用指針的原因,因?yàn)樗鼤?huì)增加一級(jí)訪問路徑,而 map , slice , interface{} 等類型是不可避免要用到的,為了減少不必要的逃逸,只能拿指針開刀了。
大多數(shù)情況下,性能優(yōu)化都會(huì)為程序帶來一定的復(fù)雜度。建議實(shí)際項(xiàng)目中還是怎么方便怎么寫,功能完成后通過性能分析找到瓶頸所在,再對(duì)局部進(jìn)行優(yōu)化。
報(bào)錯(cuò)信息如下:
[logid: 3628171316][json.err: EOF] [exception:EOF] goroutine 20521 [running]:
原因:
在請(qǐng)求體為json的請(qǐng)求中,調(diào)用了parsePostForm方法,方法大致內(nèi)容如下:
在這個(gè)方法中,會(huì)讀取http的請(qǐng)求體。
在這里被讀取的時(shí)候,后面又重新讀取一次,之后解json
后面讀到的請(qǐng)求體是空的,解json的時(shí)候就會(huì)報(bào)錯(cuò)EOF
總結(jié):golang中http請(qǐng)求報(bào)錯(cuò)eof,多半是因?yàn)槎啻巫x取了請(qǐng)求體
一.幾種公共方法
1)Print:???輸出到控制臺(tái)(不接受任何格式化,它等價(jià)于對(duì)每一個(gè)操作數(shù)都應(yīng)用?%v)
print 在golang中?是屬于輸出到標(biāo)準(zhǔn)錯(cuò)誤流中并打印,官方不建議寫程序時(shí)候用它??梢栽賒ebug時(shí)候用
2)Println:?輸出到控制臺(tái)并換行
3)Printf :?只可以打印出格式化的字符串。只可以直接輸出字符串類型的變量(不可以輸出整形變量和整形等)
4)Sprintf:格式化并返回一個(gè)字符串而不帶任何輸出
5)Fprintf:來格式化并輸出到 io.Writers 而不是 os.Stdout
二.帶占位符輸出--網(wǎng)址:? ??
和python差不多的道理,這里簡(jiǎn)單補(bǔ)充
v ????值的默認(rèn)格式
%+v???添加字段名(如結(jié)構(gòu)體)
%#v ?相應(yīng)值的Go語法表示?
%T????相應(yīng)值的類型的Go語法表示?
%%????字面上的百分號(hào),并非值的占位符
%c?????相應(yīng)Unicode碼點(diǎn)所表示的字符?
%x?????十六進(jìn)制表示,字母形式為小寫 a-f
%X?????十六進(jìn)制表示,字母形式為大寫 A-F
%U???? Unicode格式:U+1234,等同于?"U+%04X"
新聞名稱:go語言10大坑 go語言 缺點(diǎn)
分享URL:http://jinyejixie.com/article42/doppchc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、App開發(fā)、網(wǎng)站內(nèi)鏈、網(wǎng)站收錄、外貿(mào)建站、
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)