今天小編給大家分享一下golang中的gc原理是什么的相關知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
成都創(chuàng)新互聯(lián)成立于2013年,是專業(yè)互聯(lián)網(wǎng)技術服務公司,擁有項目網(wǎng)站設計制作、做網(wǎng)站網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元鏡湖做網(wǎng)站,已為上家服務,為鏡湖各地企業(yè)和個人服務,聯(lián)系電話:18980820575gc出現(xiàn)
對于任何使用C語言的人,如果問他們C語言的較大煩惱是什么,其中許多人可能會回答說是指針和內(nèi)存泄漏。以致于后出現(xiàn)的語言,都在幫助程序員來處理內(nèi)存泄漏的問題,比較有名的語言java、python、go等等,都有一個比較重要的機制,那就是gc(Garbage Collection),也就是垃圾收集器。當然這也是得意于這些語言的特質(zhì),它們在運行的時候,都有一個諸如golang中runtime的機制,java中有jvm來管理。程序員不再需要考慮,我什么時候應該分配內(nèi)存,什么時候應該回收內(nèi)存。如果是從c語言開始學習編程的,應該對malloc和free非常熟悉,再寫代碼的時候回小心翼翼的使用這兩個函數(shù)。如果是學python的,根本就不會注意到這個問題,有不會關心這個方面的問題。比較值得說明的是,在c語言中使用char列表來存儲字符串,是一件非常麻煩的事情,在java等但是,就算如此,在日常的寫程序的過程中,程序員也應該注意gc方面的問題。一個比較差的設計,可能使得gc對于整個程序的負擔非常的大,可能會發(fā)現(xiàn)程序中gc的時間占比非常高。
gc的原理
gc常見的方式有:
引用計數(shù)(reference counting)每個對象維護一個引用計數(shù)器,當引用該對象的對象被銷毀或者更新的時候,被引用對象的引用計數(shù)器自動減 1,當被應用的對象被創(chuàng)建,或者賦值給其他對象時,引用 +1,引用為 0 的時候回收,思路簡單,但是頻繁更新引用計數(shù)器降低性能,存在循環(huán)以引用(php,Python所使用的)
標記清除(mark and sweep)就是 golang 所使用的,從根變量來時遍歷所有被引用對象,標記之后進行清除操作,對未標記對象進行回收,缺點:每次垃圾回收的時候都會暫停所有的正常運行的代碼,系統(tǒng)的響應能力會大大降低,各種 mark&swamp 變種(三色標記法),緩解性能問題。
分代搜集(generation)jvm 就使用的分代回收的思路。在面向?qū)ο缶幊陶Z言中,絕大多數(shù)對象的生命周期都非常短。分代收集的基本思想是,將堆劃分為兩個或多個稱為代(generation)的空間。新創(chuàng)建的對象存放在稱為新生代(young generation)中(一般來說,新生代的大小會比 老年代小很多),隨著垃圾回收的重復執(zhí)行,生命周期較長的對象會被提升(promotion)到老年代中(這里用到了一個分類的思路,這個是也是科學思考的一個基本思路)。
golang中的gc原理
go1.3以前gc較大的問題在于stw(stop the word),即在gc的時候需要暫停程序行為,然后進標記,最后將未標記的垃圾清除。如果頻繁的觸發(fā)gc的話,程序的運行就一卡一卡的。其基本的思路就是:
1.標記:在內(nèi)存堆中(由于有的時候管理內(nèi)存頁的時候要用到堆的數(shù)據(jù)結(jié)構(gòu),所以稱為堆內(nèi)存)存儲著有一系列的對象,這些對象可能會與其他對象有關聯(lián)(references between these objects) a tracing garbage collector 會在某一個時間點上停止原本正在運行的程序,之后它會掃描 runtim e已經(jīng)知道的的 object 集合(already known set of objects),通常它們是存在于 stack 中的全局變量以及各種對象。gc 會對這些對象進行標記,將這些對象的狀態(tài)標記為可達,從中找出所有的,從當前的這些對象可以達到其他地方的對象的 reference,并且將這些對象也標記為可達的對象,這個步驟被稱為 mark phase,即標記階段,這一步的主要目的是用于獲取這些對象的狀態(tài)信息。
2.回收:一旦將所有的這些對象都掃描完,gc 就會獲取到所有的無法 reach 的對象(狀態(tài)為 unreachable 的對象),并且將它們回收,這一步稱為 sweep phase,即是清掃階段。
3.清除:gc 僅僅搜集那些未被標記為可達(reachable)的對象。如果 gc 沒有識別出一個 reference,最后有可能會將一個仍然在使用的對象給回收掉,就引起了程序運行錯誤。
go在1.3的時候引入了并發(fā)清理,go team 自己的說法是減少了 50%-70% 的暫停時間。
go在1.5時候使用了三色標記法,這個是標記清除算法的一個升級變種。流程如下:
1.灰色:對象已被標記,但這個對象包含的子對象未標記
2.黑色:對象已被標記,且這個對象包含的子對象也已標記,gcmarkBits對應的位為1(該對象不會在本次GC中被清理)
3.白色:對象未被標記,gcmarkBits對應的位為0(該對象將會在本次GC中被清理)
例如,當前內(nèi)存中有A~F一共6個對象,根對象a,b本身為棧上分配的局部變量,根對象a、b分別引用了對象A、B, 而B對象又引用了對象D,則GC開始前各對象的狀態(tài)如下圖所示:
1.初始狀態(tài)下所有對象都是白色的。
2.接著開始掃描根對象a、b; 由于根對象引用了對象A、B,那么A、B變?yōu)榛疑珜ο?,接下來就開始分析灰色對象,分析A時,A沒有引用其他對象很快就轉(zhuǎn)入黑色,B引用了D,則B轉(zhuǎn)入黑色的同時還需要將D轉(zhuǎn)為灰色,進行接下來的分析。
3.灰色對象只有D,由于D沒有引用其他對象,所以D轉(zhuǎn)入黑色。標記過程結(jié)束
4.最終,黑色的對象會被保留下來,白色對象會被回收掉。
go中的gc過程
GO的GC是并行GC, 也就是GC的大部分處理和普通的go代碼是同時運行的, 這讓GO的GC流程比較復雜。
1.Stack scan:Collect pointers from globals and goroutine stacks。收集根對象(全局變量,和G stack),開啟寫屏障。全局變量、開啟寫屏障需要STW,G stack只需要停止該G就好,時間比較少。
2.Mark: Mark objects and follow pointers。標記所有根對象, 和根對象可以到達的所有對象不被回收。
3.Mark Termination: Rescan globals/changed stack, finish mark。重新掃描全局變量,和上一輪改變的stack(寫屏障),完成標記工作。這個過程需要STW。
4.Sweep: 按標記結(jié)果清掃span
從1.8以后的golang將第一步的stop the world 也取消了,這又是一次優(yōu)化;1.9開始, 寫屏障的實現(xiàn)使用了Hybrid Write Barrier, 大幅減少了第二次STW的時間.
因為go支持并行GC, GC的掃描和go代碼可以同時運行, 這樣帶來的問題是GC掃描的過程中go代碼有可能改變了對象的依賴樹。
例如開始掃描時發(fā)現(xiàn)根對象A和B, B擁有C的指針。
1.GC先掃描A,A放入黑色
2.B把C的指針交給A
3.GC再掃描B,B放入黑色
4.C在白色,會回收;但是A其實引用了C。
為了避免這個問題, go在GC的標記階段會啟用寫屏障(Write Barrier)。啟用了寫屏障(Write Barrier)后,在GC第三輪rescan階段,根據(jù)寫屏障標記將C放入灰色,防止C丟失。
以上就是“golang中的gc原理是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。