下面由golang教程欄目給大家介紹關(guān)于Golang GC 垃圾回收機(jī)制的詳解,希望對需要的朋友有所幫助!
10余年的鳳岡網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。成都營銷網(wǎng)站建設(shè)的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整鳳岡建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)從事“鳳岡網(wǎng)站設(shè)計”,“鳳岡網(wǎng)站推廣”以來,每個客戶項目都認(rèn)真落實執(zhí)行。摘要在實際使用 go 語言的過程中,碰到了一些看似奇怪的內(nèi)存占用現(xiàn)象,于是決定對go語言的垃圾回收模型進(jìn)行一些研究。本文對研究的結(jié)果進(jìn)行一下總結(jié)。
什么是垃圾回收?曾幾何時,內(nèi)存管理是程序員開發(fā)應(yīng)用的一大難題。傳統(tǒng)的系統(tǒng)級編程語言(主要指C/C++)中,程序員必須對內(nèi)存小心的進(jìn)行管理操作,控制內(nèi)存的申請及釋放。稍有不慎,就可能產(chǎn)生內(nèi)存泄露問題,這種問題不易發(fā)現(xiàn)并且難以定位,一直成為困擾開發(fā)者的噩夢。如何解決這個頭疼的問題呢?過去一般采用兩種辦法:
內(nèi)存泄露檢測工具。這種工具的原理一般是靜態(tài)代碼掃描,通過掃描程序檢測可能出現(xiàn)內(nèi)存泄露的代碼段。然而檢測工具難免有疏漏和不足,只能起到輔助作用。智能指針。這是 c++ 中引入的自動內(nèi)存管理方法,通過擁有自動內(nèi)存管理功能的指針對象來引用對象,是程序員不用太關(guān)注內(nèi)存的釋放,而達(dá)到內(nèi)存自動釋放的目的。這種方法是采用最廣泛的做法,但是對程序員有一定的學(xué)習(xí)成本(并非語言層面的原生支持),而且一旦有忘記使用的場景依然無法避免內(nèi)存泄露。為了解決這個問題,后來開發(fā)出來的幾乎所有新語言(java,python,php等等)都引入了語言層面的自動內(nèi)存管理 – 也就是語言的使用者只用關(guān)注內(nèi)存的申請而不必關(guān)心內(nèi)存的釋放,內(nèi)存釋放由虛擬機(jī)(virtual machine)或運行時(runtime)來自動進(jìn)行管理。而這種對不再使用的內(nèi)存資源進(jìn)行自動回收的行為就被稱為垃圾回收。
常見的垃圾回收方法引用計數(shù)(reference counting)這是最簡單的一種垃圾回收算法,和之前提到的智能指針異曲同工。對每個對象維護(hù)一個引用計數(shù),當(dāng)引用該對象的對象被銷毀或更新時被引用對象的引用計數(shù)自動減一,當(dāng)被引用對象被創(chuàng)建或被賦值給其他對象時引用計數(shù)自動加一。當(dāng)引用計數(shù)為0時則立即回收對象。
這種方法的優(yōu)點是實現(xiàn)簡單,并且內(nèi)存的回收很及時。這種算法在內(nèi)存比較緊張和實時性比較高的系統(tǒng)中使用的比較廣泛,如ios cocoa框架,php,python等。簡單引用計數(shù)算法也有明顯的缺點:
頻繁更新引用計數(shù)降低了性能。一種簡單的解決方法就是編譯器將相鄰的引用計數(shù)更新操作合并到一次更新;還有一種方法是針對頻繁發(fā)生的臨時變量引用不進(jìn)行計數(shù),而是在引用達(dá)到0時通過掃描堆棧確認(rèn)是否還有臨時對象引用而決定是否釋放。等等還有很多其他方法,具體可以參考這里。循環(huán)引用問題。當(dāng)對象間發(fā)生循環(huán)引用時引用鏈中的對象都無法得到釋放。最明顯的解決辦法是避免產(chǎn)生循環(huán)引用,如cocoa引入了strong指針和weak指針兩種指針類型。或者系統(tǒng)檢測循環(huán)引用并主動打破循環(huán)鏈。當(dāng)然這也增加了垃圾回收的復(fù)雜度。標(biāo)記-清除(mark and sweep)該方法分為兩步,標(biāo)記從根變量開始迭代得遍歷所有被引用的對象,對能夠通過應(yīng)用遍歷訪問到的對象都進(jìn)行標(biāo)記為“被引用”;標(biāo)記完成后進(jìn)行清除操作,對沒有標(biāo)記過的內(nèi)存進(jìn)行回收(回收同時可能伴有碎片整理操作)。這種方法解決了引用計數(shù)的不足,但是也有比較明顯的問題:每次啟動垃圾回收都會暫停當(dāng)前所有的正常代碼執(zhí)行,回收是系統(tǒng)響應(yīng)能力大大降低!當(dāng)然后續(xù)也出現(xiàn)了很多mark&sweep算法的變種(如三色標(biāo)記法)優(yōu)化了這個問題。
分代收集(generation)經(jīng)過大量實際觀察得知,在面向?qū)ο缶幊陶Z言中,絕大多數(shù)對象的生命周期都非常短。分代收集的基本思想是,將堆劃分為兩個或多個稱為 代(generation)的空間。新創(chuàng)建的對象存放在稱為 新生代(young generation)中(一般來說,新生代的大小會比 老年代小很多),隨著垃圾回收的重復(fù)執(zhí)行,生命周期較長的對象會被 提升(promotion)到老年代中。因此,新生代垃圾回收和老年代垃圾回收兩種不同的垃圾回收方式應(yīng)運而生,分別用于對各自空間中的對象執(zhí)行垃圾回收。新生代垃圾回收的速度非??欤壤夏甏鞄讉€數(shù)量級,即使新生代垃圾回收的頻率更高,執(zhí)行效率也仍然比老年代垃圾回收強(qiáng),這是因為大多數(shù)對象的生命周期都很短,根本無需提升到老年代。
GO的垃圾回收器go語言垃圾回收總體采用的是經(jīng)典的mark and sweep算法。
1.3版本以前,golang的垃圾回收算法都非常簡陋,然后其性能也廣被詬病:go runtime在一定條件下(內(nèi)存超過閾值或定期如2min),暫停所有任務(wù)的執(zhí)行,進(jìn)行mark&sweep操作,操作完成后啟動所有任務(wù)的執(zhí)行。在內(nèi)存使用較多的場景下,go程序在進(jìn)行垃圾回收時會發(fā)生非常明顯的卡頓現(xiàn)象(Stop The World)。在對響應(yīng)速度要求較高的后臺服務(wù)進(jìn)程中,這種延遲簡直是不能忍受的!這個時期國內(nèi)外很多在生產(chǎn)環(huán)境實踐go語言的團(tuán)隊都或多或少踩過gc的坑。當(dāng)時解決這個問題比較常用的方法是盡快控制自動分配內(nèi)存的內(nèi)存數(shù)量以減少gc負(fù)荷,同時采用手動管理內(nèi)存的方法處理需要大量及高頻分配內(nèi)存的場景。1.3版本開始go team開始對gc性能進(jìn)行持續(xù)的改進(jìn)和優(yōu)化,每個新版本的go發(fā)布時gc改進(jìn)都成為大家備受關(guān)注的要點。1.3版本中,go runtime分離了mark和sweep操作,和以前一樣,也是先暫停所有任務(wù)執(zhí)行并啟動mark,mark完成后馬上就重新啟動被暫停的任務(wù)了,而是讓sweep任務(wù)和普通協(xié)程任務(wù)一樣并行的和其他任務(wù)一起執(zhí)行。如果運行在多核處理器上,go會試圖將gc任務(wù)放到單獨的核心上運行而盡量不影響業(yè)務(wù)代碼的執(zhí)行。go team自己的說法是減少了50%-70%的暫停時間。1.4版本(當(dāng)前最新穩(wěn)定版本)對gc的性能改動并不多。1.4版本中runtime很多代碼取代了原生c語言實現(xiàn)而采用了go語言實現(xiàn),對gc帶來的一大改變是可以是實現(xiàn)精確的gc。c語言實現(xiàn)在gc時無法獲取到內(nèi)存的對象信息,因此無法準(zhǔn)確區(qū)分普通變量和指針,只能將普通變量當(dāng)做指針,如果碰巧這個普通變量指向的空間有其他對象,那這個對象就不會被回收。而go語言實現(xiàn)是完全知道對象的類型信息,在標(biāo)記時只會遍歷指針指向的對象,這樣就避免了C實現(xiàn)時的堆內(nèi)存浪費(解決約10-30%)。1.5版本go team對gc又進(jìn)行了比較大的改進(jìn)(1.4中已經(jīng)埋下伏筆如write barrier的引入),官方的主要目標(biāo)是減少延遲。go 1.5正在實現(xiàn)的垃圾回收器是“非分代的、非移動的、并發(fā)的、三色的標(biāo)記清除垃圾收集器”。分代算法上文已經(jīng)提及,是一種比較好的垃圾回收管理策略,然1.5版本中并未考慮實現(xiàn);我猜測的原因是步子不能邁太大,得逐步改進(jìn),go官方也表示會在1.6版本的gc優(yōu)化中考慮。同時引入了上文介紹的三色標(biāo)記法,這種方法的mark操作是可以漸進(jìn)執(zhí)行的而不需每次都掃描整個內(nèi)存空間,可以減少stop the world的時間。 由此可以看到,一路走來直到1.5版本,go的垃圾回收性能也是一直在提升,但是相對成熟的垃圾回收系統(tǒng)(如java jvm和javascript v8),go需要優(yōu)化的路徑還很長(但是相信未來一定是美好的~)。實踐經(jīng)驗團(tuán)隊在實踐go語言時同樣碰到最多和最棘手的問題也是內(nèi)存問題(其中g(shù)c為主),這里把遇到的問題和經(jīng)驗總結(jié)下,歡迎大家一起交流探討。
go程序內(nèi)存占用大的問題這個問題在我們對后臺服務(wù)進(jìn)行壓力測試時發(fā)現(xiàn),我們模擬大量的用戶請求訪問后臺服務(wù),這時各服務(wù)模塊能觀察到明顯的內(nèi)存占用上升。但是當(dāng)停止壓測時,內(nèi)存占用并未發(fā)生明顯的下降?;撕荛L時間定位問題,使用gprof等各種方法,依然沒有發(fā)現(xiàn)原因。最后發(fā)現(xiàn)原來這時正常的…主要的原因有兩個,
一是go的垃圾回收有個觸發(fā)閾值,這個閾值會隨著每次內(nèi)存使用變大而逐漸增大(如初始閾值是10MB則下一次就是20MB,再下一次就成為了40MB…),如果長時間沒有觸發(fā)gc go會主動觸發(fā)一次(2min)。高峰時內(nèi)存使用量上去后,除非持續(xù)申請內(nèi)存,靠閾值觸發(fā)gc已經(jīng)基本不可能,而是要等最多2min主動gc開始才能觸發(fā)gc。
第二個原因是go語言在向系統(tǒng)交還內(nèi)存時只是告訴系統(tǒng)這些內(nèi)存不需要使用了,可以回收;同時操作系統(tǒng)會采取“拖延癥”策略,并不是立即回收,而是等到系統(tǒng)內(nèi)存緊張時才會開始回收這樣該程序又重新申請內(nèi)存時就可以獲得極快的分配速度。
gc時間長的問題對于對用戶響應(yīng)事件有要求的后端程序,golang gc時的stop the world兼職是噩夢。根據(jù)上文的介紹,1.5版本的go再完成上述改進(jìn)后應(yīng)該gc性能會提升不少,但是所有的垃圾回收型語言都難免在gc時面臨性能下降,對此我們對于應(yīng)該盡量避免頻繁創(chuàng)建臨時堆對象(如&abc{}, new, make等)以減少垃圾收集時的掃描時間,對于需要頻繁使用的臨時對象考慮直接通過數(shù)組緩存進(jìn)行重用;很多人采用cgo的方法自己管理內(nèi)存而繞開垃圾收集,這種方法除非迫不得已個人是不推薦的(容易造成不可預(yù)知的問題),當(dāng)然迫不得已的情況下還是可以考慮的,這招帶來的效果還是很明顯的~
goroutine泄露的問題我們的一個服務(wù)需要處理很多長連接請求,實現(xiàn)時,對于每個長連接請求各開了一個讀取和寫入?yún)f(xié)程,全部采用endless for loop不停地處理收發(fā)數(shù)據(jù)。當(dāng)連接被遠(yuǎn)端關(guān)閉后,如果不對這兩個協(xié)程做處理,他們依然會一直運行,并且占用的channel也不會被釋放…這里就必須十分注意,在不使用協(xié)程后一定要把他依賴的channel close并通過再協(xié)程中判斷channel是否關(guān)閉以保證其退出。
Golang-gc基本知識APR 30TH, 2016 8:02 PM | COMMENTS
這一部分主要介紹golang gc的一些入門的相關(guān)知識,由于gc內(nèi)容涉及比較多,一點一點慢慢整理。
Golang GC的背景golang是基于garbage collection的語言,這是它的設(shè)計原則。作為一個有垃圾回收器的語言,gc與程序交互時候的效率會影響到整個程序的運行效率。通常程序本身的內(nèi)存管理會影響gc和程序之間的效率,甚至造成性能瓶頸。Golang GC的相關(guān)問題主要參的這個:
http://morsmachine.dk/machine-gc
是14年寫的,估計那個時候的gc機(jī)制還比較simple,新版本的golang對gc的改動應(yīng)該會比較大
還有那個go語言讀書筆記中關(guān)于golang gc 的相關(guān)部分
關(guān)于內(nèi)存泄露“內(nèi)存泄露”(Memory Leak)這個詞看似自己很熟悉,可實際上卻也從沒有看過它的準(zhǔn)確含義。
內(nèi)存泄露,是從操作系統(tǒng)的角度上來闡述的,形象的比喻就是“操作系統(tǒng)可提供給所有進(jìn)程的存儲空間(虛擬內(nèi)存空間)正在被某個進(jìn)程榨干”,導(dǎo)致的原因就是程序在運行的時候,會不斷地動態(tài)開辟的存儲空間,這些存儲空間在在運行結(jié)束之后后并沒有被及時釋放掉。應(yīng)用程序在分配了某段內(nèi)存之后,由于設(shè)計的錯誤,會導(dǎo)致程序失去了對該段內(nèi)存的控制,造成了內(nèi)存空間的浪費。
如果程序在內(nèi)存空間內(nèi)申請了一塊內(nèi)存,之后程序運行結(jié)束之后,沒有把這塊內(nèi)存空間釋放掉,而且對應(yīng)的程序又沒有很好的gc機(jī)制去對程序申請的空間進(jìn)行回收,這樣就會導(dǎo)致內(nèi)存泄露。
從用戶的角度來說,內(nèi)存泄露本身不會有什么危害,因為這不是對用戶功能的影響,但是“內(nèi)存泄露”如果進(jìn)
對于 C 和 C++ 這種沒有 Garbage Collection 的語言來講,我們主要關(guān)注兩種類型的內(nèi)存泄漏:
堆內(nèi)存泄漏(Heap leak)。對內(nèi)存指的是程序運行中根據(jù)需要分配通過 malloc,realloc new 等從堆中分配的一塊內(nèi)存,再是完成后必須通過調(diào)用對應(yīng)的 free 或者 delete 刪掉。如果程序的設(shè)計的錯誤導(dǎo)致這部分內(nèi)存沒有被釋放,那么此后這塊內(nèi)存將不會被使用,就會產(chǎn)生 Heap Leak.系統(tǒng)資源泄露(Resource Leak).主要指程序使用系統(tǒng)分配的資源比如 Bitmap,handle ,SOCKET 等沒有使用相應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費,嚴(yán)重可導(dǎo)致系統(tǒng)效能降低,系統(tǒng)運行不穩(wěn)定。內(nèi)存泄露涉及到的相關(guān)問題還有很多,這里暫不展開討論。
常見的 GC 模式具體的優(yōu)缺點可以參考這個,這里只是進(jìn)行大致介紹。
引用計數(shù)(reference counting)每個對象維護(hù)一個引用計數(shù)器,當(dāng)引用該對象的對象被銷毀或者更新的時候,被引用對象的引用計數(shù)器自動減 1,當(dāng)被應(yīng)用的對象被創(chuàng)建,或者賦值給其他對象時,引用 +1,引用為 0 的時候回收,思路簡單,但是頻繁更新引用計數(shù)器降低性能,存在循環(huán)以引用(php,Python所使用的)標(biāo)記清除(mark and sweep)就是 golang 所使用的,從根變量來時遍歷所有被引用對象,標(biāo)記之后進(jìn)行清除操作,對未標(biāo)記對象進(jìn)行回收,缺點:每次垃圾回收的時候都會暫停所有的正常運行的代碼,系統(tǒng)的響應(yīng)能力會大大降低,各種 mark&swamp 變種(三色標(biāo)記法),緩解性能問題。分代搜集(generation)jvm 就使用的分代回收的思路。在面向?qū)ο缶幊陶Z言中,絕大多數(shù)對象的生命周期都非常短。分代收集的基本思想是,將堆劃分為兩個或多個稱為代(generation)的空間。新創(chuàng)建的對象存放在稱為新生代(young generation)中(一般來說,新生代的大小會比 老年代小很多),隨著垃圾回收的重復(fù)執(zhí)行,生命周期較長的對象會被提升(promotion)到老年代中(這里用到了一個分類的思路,這個是也是科學(xué)思考的一個基本思路)。因此,新生代垃圾回收和老年代垃圾回收兩種不同的垃圾回收方式應(yīng)運而生(先分類,之后再對癥下藥),分別用于對各自空間中的對象執(zhí)行垃圾回收。新生代垃圾回收的速度非???,比老年代快幾個數(shù)量級,即使新生代垃圾回收的頻率更高,執(zhí)行效率也仍然比老年代垃圾回收強(qiáng),這是因為大多數(shù)對象的生命周期都很短,根本無需提升到老年代。
golang 中的 gc 通常是如何工作的golang 中的 gc 基本上是標(biāo)記清除的思路:
在內(nèi)存堆中(由于有的時候管理內(nèi)存頁的時候要用到堆的數(shù)據(jù)結(jié)構(gòu),所以稱為堆內(nèi)存)存儲著有一系列的對象,這些對象可能會與其他對象有關(guān)聯(lián)(references between these objects) a tracing garbage collector 會在某一個時間點上停止原本正在運行的程序,之后它會掃描 runtim e已經(jīng)知道的的 object 集合(already known set of objects),通常它們是存在于 stack 中的全局變量以及各種對象。gc 會對這些對象進(jìn)行標(biāo)記,將這些對象的狀態(tài)標(biāo)記為可達(dá),從中找出所有的,從當(dāng)前的這些對象可以達(dá)到其他地方的對象的 reference,并且將這些對象也標(biāo)記為可達(dá)的對象,這個步驟被稱為 mark phase,即標(biāo)記階段,這一步的主要目的是用于獲取這些對象的狀態(tài)信息。
一旦將所有的這些對象都掃描完,gc 就會獲取到所有的無法 reach 的對象(狀態(tài)為 unreachable 的對象),并且將它們回收,這一步稱為 sweep phase,即是清掃階段。
gc 僅僅搜集那些未被標(biāo)記為可達(dá)(reachable)的對象。如果 gc 沒有識別出一個 reference,最后有可能會將一個仍然在使用的對象給回收掉,就引起了程序運行錯誤。
可以看到主要的三個步驟:掃描,回收,清掃。
感覺比起其他的語言,golang 中的垃圾回收模型還是相對簡單的。
gc中的問題gc 的引入可以說就是為了解決內(nèi)存回收的問題。新開發(fā)的語言(java,python,php等等),在使用的時候,可以使用戶不必關(guān)心內(nèi)存對象的釋放,只需要關(guān)心對象的申請即可,通過在 runtime 或者在 vm 中進(jìn)行相關(guān)的操作,達(dá)到自動管理內(nèi)存空間的效果,這種對不再使用的內(nèi)存資源進(jìn)行自動回收的行為就被稱為垃圾回收。
根據(jù)前面的表述,能否正常識別一個 reference 是 gc 能夠正常工作的基礎(chǔ),因此第一個問題就是 gc 應(yīng)該如何識別一個 reference?
的問題:對于 reference 的識別比較難,machine code 很難知道,怎樣才算是一個reference。如果錯漏掉了一個 reference,就會使得,原本沒有準(zhǔn)備好要被 free 掉的內(nèi)存現(xiàn)在被錯誤地 free 掉,所以策略就是寧多勿少。
一種策略是把所有的 memory 空間都看做是有可能的 references(指針值)。這種被稱為保守型垃圾回收器(conservative garbage collector)。C 中的 Boehm garbage collector 就是這樣工作的。就是說把內(nèi)存中的普通變量也當(dāng)做指針一樣去處理,盡量 cover 到所有的指針的情況,如果碰巧這個普通的變量值所指向的空間有其他的對象,那么這個對象是不會被回收的。而 go 語言實現(xiàn)是完全知道對象的類型信息,在標(biāo)記時只會遍歷指針指向的對象,這樣就避免了 C 實現(xiàn)時的堆內(nèi)存浪費(解決約 10-30% )。
三色標(biāo)記2014/6 1.3 引入并發(fā)清理(垃圾回收和用戶邏輯并發(fā)執(zhí)行?)
2015/8 1.5 引入三色標(biāo)記法
關(guān)于并發(fā)清理的引入,參照的是這里在 1.3 版本中,go runtime 分離了 mark 和 sweep 的操作,和以前一樣,也是先暫停所有任務(wù)執(zhí)行并啟動 mark( mark 這部分還是要把原程序停下來的),mark 完成后就馬上就重新啟動被暫停的任務(wù)了,并且讓 sweep 任務(wù)和普通協(xié)程任務(wù)一樣并行,和其他任務(wù)一起執(zhí)行。如果運行在多核處理器上,go 會試圖將 gc 任務(wù)放到單獨的核心上運行而盡量不影響業(yè)務(wù)代碼的執(zhí)行,go team 自己的說法是減少了 50%-70% 的暫停時間。
基本算法就是之前提到的清掃+回收,Golang gc 優(yōu)化的核心就是盡量使得 STW(Stop The World) 的時間越來越短。
如何測量 GC之前說了那么多,那如何測量 gc 的之星效率,判斷它到底是否對程序的運行造成了影響呢? 第一種方式是設(shè)置 godebug 的環(huán)境變量,具體可以參考這一篇,真的是講的很好的文章:鏈接,比如運行GODEBUG=gctrace=1 ./myserver
,如果要想對于輸出結(jié)果了解,還需要對于 gc 的原理進(jìn)行更進(jìn)一步的深入分析,這篇文章的好處在于,清晰的之處了 golang 的 gc 時間是由哪些因素決定的,因此也可以針對性的采取不同的方式提升 gc 的時間:
根據(jù)之前的分析也可以知道,golang 中的 gc 是使用標(biāo)記清楚法,所以 gc 的總時間為:
Tgc = Tseq + Tmark + Tsweep
( T 表示 time)
之后粒度進(jìn)一步細(xì)分,具體的概念還是有些不太懂:
與 Tmark 相關(guān)的:1 垃圾回收過程中,堆中活動對象的數(shù)量,2 帶有指針的活動對象占據(jù)的內(nèi)存總量 3 活動對象中的指針數(shù)量。與 Tsweep 相關(guān)的:1 堆內(nèi)存的總量 2 堆中的垃圾總量如何進(jìn)行 gc 調(diào)優(yōu)( gopher 大會 Danny )硬性參數(shù)涉及算法的問題,總是會有些參數(shù)。GOGC 參數(shù)主要控制的是下一次 gc 開始的時候的內(nèi)存使用量。
比如當(dāng)前的程序使用了 4M 的對內(nèi)存(這里說的是堆內(nèi)存),即是說程序當(dāng)前 reachable 的內(nèi)存為 4m,當(dāng)程序占用的內(nèi)存達(dá)到 reachable*(1+GOGC/100)=8M 的時候,gc 就會被觸發(fā),開始進(jìn)行相關(guān)的 gc 操作。
如何對 GOGC 的參數(shù)進(jìn)行設(shè)置,要根據(jù)生產(chǎn)情況中的實際場景來定,比如 GOGC 參數(shù)提升,來減少 GC 的頻率。
小tips想要有深入的 insights,使用 gdb 時必不可少的了,這篇文章里面整理了一些 gdb 使用的入門技巧。
減少對象分配 所謂減少對象的分配,實際上是盡量做到,對象的重用。 比如像如下的兩個函數(shù)定義:
第一個函數(shù)沒有形參,每次調(diào)用的時候返回一個 []byte,第二個函數(shù)在每次調(diào)用的時候,形參是一個 buf []byte 類型的對象,之后返回讀入的 byte 的數(shù)目。
第一個函數(shù)在每次調(diào)用的時候都會分配一段空間,這會給 gc 造成額外的壓力。第二個函數(shù)在每次迪調(diào)用的時候,會重用形參聲明。
老生常談 string 與 []byte 轉(zhuǎn)化 在 stirng 與 []byte 之間進(jìn)行轉(zhuǎn)換,會給 gc 造成壓力 通過 gdb,可以先對比下兩者的數(shù)據(jù)結(jié)構(gòu):
兩者發(fā)生轉(zhuǎn)換的時候,底層數(shù)據(jù)結(jié)結(jié)構(gòu)會進(jìn)行復(fù)制,因此導(dǎo)致 gc 效率會變低。解決策略上,一種方式是一直使用 []byte,特別是在數(shù)據(jù)傳輸方面,[]byte 中也包含著許多 string 會常用到的有效的操作。另一種是使用更為底層的操作直接進(jìn)行轉(zhuǎn)化,避免復(fù)制行為的發(fā)生??梢詤⒖嘉⑿拧坝旰蹖W(xué)堂”中性能優(yōu)化的第一部分,主要是使用 unsafe.Pointer 直接進(jìn)行轉(zhuǎn)化。
對于 unsafe 的使用,感覺可以單獨整理一出一篇文章來了,先把相關(guān)資料列在這里 http://studygolang.com/articles/685 直觀上,可以把 unsafe.Pointer 理解成 c++ 中的 void*,在 golang 中,相當(dāng)于是各種類型的指針進(jìn)行轉(zhuǎn)化的橋梁。
關(guān)于 uintptr 的底層類型是 int,它可以裝下指針?biāo)傅牡刂返闹?。它可以?unsafe.Pointer 進(jìn)行相互轉(zhuǎn)化,主要的區(qū)別是,uintptr 可以參與指針運算,而 unsafe.Pointer 只能進(jìn)行指針轉(zhuǎn)化,不能進(jìn)行指針運算。想要用 golang 進(jìn)行指針運算,可以參考這個。具體指針運算的時候,要先轉(zhuǎn)成 uintptr 的類型,才能進(jìn)一步計算,比如偏移多少之類的。
少量使用+連接 string 由于采用 + 來進(jìn)行 string 的連接會生成新的對象,降低 gc 的效率,好的方式是通過 append 函數(shù)來進(jìn)行。
但是還有一個弊端,比如參考如下代碼:
在使用了append操作之后,數(shù)組的空間由1024增長到了1312,所以如果能提前知道數(shù)組的長度的話,最好在最初分配空間的時候就做好空間規(guī)劃操作,會增加一些代碼管理的成本,同時也會降低gc的壓力,提升代碼的效率。
文章題目:關(guān)于GolangGC垃圾回收機(jī)制的詳解
瀏覽路徑:http://www.rwnh.cn/article36/cpeipg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、營銷型網(wǎng)站建設(shè)、建站公司、響應(yīng)式網(wǎng)站、關(guān)鍵詞優(yōu)化、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)