1、簡述 Go 語言GC(垃圾回收)的工作原理
Go1.3采用標記清除法伟件, Go1.5采用三色標記法殿雪,Go1.8采用三色標記法+混合寫屏障。
1. 標記清除法
分為兩個階段:標記和清除
- 標記階段:從根對象出發(fā)尋找并標記所有存活的對象锋爪;
- 清除階段:遍歷堆中的對象,回收未標記的對象爸业,并加入空閑鏈表其骄;
- 缺點是需要暫停程序STW。
2. 三色標記法:
將對象標記為白色扯旷,灰色或黑色拯爽。
白色:不確定對象(默認色);黑色:存活對象钧忽√号冢灰色:存活對象逼肯,子對象待處理。
標記開始時桃煎,先將所有對象加入白色集合(需要STW)篮幢。首先將根對象標記為灰色,然后將一個對象從灰色集合取出为迈,遍歷其子對象三椿,放入灰色集合。同時將取出的對象放入黑色集合葫辐,直到灰色集合為空搜锰。最后的白色集合對象就是需要清理的對象。
這種方法有一個缺陷耿战,如果對象的引用被用戶修改了蛋叼,那么之前的標記就無效了。因此Go采用了寫屏障技術(shù)剂陡,當對象新增或者更新會將其著色為灰色狈涮。
一次完整的GC分為四個階段:
- 準備標記(需要STW),開啟寫屏障鹏倘。
- 開始標記
- 標記結(jié)束(STW)薯嗤,關(guān)閉寫屏障
- 清理(并發(fā))
基于插入寫屏障和刪除寫屏障在結(jié)束時需要STW來重新掃描棧,帶來性能瓶頸纤泵。
混合寫屏障分為以下四步:
- GC開始時骆姐,將棧上的全部對象標記為黑色(不需要二次掃描,無需STW)捏题;
- GC期間玻褪,任何棧上創(chuàng)建的新對象均為黑色;
- 被刪除引用的對象標記為灰色公荧;
- 被添加引用的對象標記為灰色带射;
- 總而言之就是確保黑色對象不能引用白色對象,這個改進直接使得GC時間從 2s降低到2us循狰。
2窟社、Go 中 make 和 new 的區(qū)別?
- 共同點:給變量分配內(nèi)存绪钥。
- 不同點:
1.作用變量類型不同灿里,new給string,int和數(shù)組分配內(nèi)存,make給切片程腹,map匣吊,channel分配內(nèi)存;
2.返回類型不一樣,new返回指向變量的指針色鸳,make返回變量本身社痛;
3.new 分配的空間被清零。make 分配空間后命雀,會進行初始化蒜哀。
3、數(shù)組和切片的區(qū)別?
- 相同點:
1.只能存儲一組相同類型的數(shù)據(jù)結(jié)構(gòu);
2.都是通過下標來訪問咏雌,并且有容量長度凡怎,長度通過 len 獲取,容量通過 cap 獲取赊抖。 - 不同點:
1.數(shù)組是定長统倒,訪問和復(fù)制不能超過數(shù)組定義的長度,否則就會下標越界氛雪,切片長度和容量可以自動擴容房匆;
2.數(shù)組是值類型,切片是引用類型报亩,每個切片都引用了一個底層數(shù)組浴鸿,切片本身不能存儲任何數(shù)據(jù),都是這底層數(shù)組存儲數(shù)據(jù)弦追,所以修改切片的時候修改的是底層數(shù)組中的數(shù)據(jù)岳链。切片一旦擴容,指向一個新的底層數(shù)組劲件,內(nèi)存地址也就隨之改變掸哑。
4、能介紹下 rune 類型嗎零远?
golang中的字符串底層實現(xiàn)是通過byte數(shù)組的苗分,中文字符在unicode下占2個字節(jié),在utf-8編碼下占3個字節(jié)牵辣,而golang默認編碼正好是utf-8摔癣。
- byte 等同于int8,常用來處理ascii字符纬向;
- rune 等同于int32,常用來處理unicode或utf-8字符择浊。
5、Go 的 slice 底層數(shù)據(jù)結(jié)構(gòu)和一些特性逾条?
Go 的 slice 底層數(shù)據(jù)結(jié)構(gòu)是由一個 array 指針指向底層數(shù)組琢岩,len 表示切片長度,cap 表示切片容量膳帕。
slice 的主要實現(xiàn)是擴容。對于 append 向 slice 添加元素時,假如 slice 容量夠用危彩,則追加新元素進去攒磨,slice.len++,返回原來的 slice汤徽。當原容量不夠娩缰,則 slice 先擴容,擴容之后 slice 得到新的 slice谒府,將元素追加進新的 slice拼坎,slice.len++,返回新的 slice完疫。
對于切片的擴容規(guī)則:當切片比較小時(容量小于 1024)泰鸡,則采用較大的擴容倍速進行擴容(新的擴容會是原來的 2 倍),避免頻繁擴容壳鹤,從而減少內(nèi)存分配的次數(shù)和數(shù)據(jù)拷貝的代價盛龄。當切片較大的時(原來的 slice 的容量大于或者等于 1024),采用較小的擴容倍速(新的擴容將擴大大于或者等于原來 1.25 倍)芳誓,主要避免空間浪費余舶,網(wǎng)上其實很多總結(jié)的是 1.25 倍,那是在不考慮內(nèi)存對齊的情況下锹淌,實際上還要考慮內(nèi)存對齊匿值,擴容是大于或者等于 1.25 倍。
6赂摆、Go 的 defer 底層數(shù)據(jù)結(jié)構(gòu)和特性挟憔?
每個 defer 語句都對應(yīng)一個_defer 實例,多個實例使用指針連接起來形成一個單鏈表库正,保存在 gotoutine 數(shù)據(jù)結(jié)構(gòu)中曲楚,每次插入_defer 實例,均插入到鏈表的頭部褥符,函數(shù)結(jié)束再一次從頭部取出龙誊,從而形成后進先出的效果。
defer 的規(guī)則總結(jié):
- 延遲函數(shù)的參數(shù)是 defer 語句出現(xiàn)的時候就已經(jīng)確定喷楣;
- 延遲函數(shù)執(zhí)行按照后進先出的順序執(zhí)行趟大;
- 延遲函數(shù)可能操作主函數(shù)的返回值;
- 申請資源后立即使用 defer 關(guān)閉資源铣焊。