福哥答案2020-11-19:
- 什么是defer
- defer是go語言提供的一種用于注冊延遲調(diào)用的機(jī)制:讓函數(shù)或者語句在當(dāng)前函數(shù)執(zhí)行完畢(包括return正常結(jié)束或者panic導(dǎo)致的異常結(jié)束)之后執(zhí)行。
- defer語句通常用于一些成對的操作場景,打開/關(guān)閉連接,加鎖/解鎖,打開文件/關(guān)閉文件等等
- defer在一些需要回收資源的場景中非常有用
- 為什么需要defer
- 有效防止內(nèi)存泄漏
- defer底層原理
- 每次defer語句在執(zhí)行的時(shí)候,都會將函數(shù)進(jìn)行“壓棧”,函數(shù)參數(shù)會被拷貝下來。當(dāng)外層函數(shù)退出時(shí),defer函數(shù)會按照定義的順序逆序執(zhí)行。如果defer執(zhí)行的函數(shù)為nil,那么會在最終調(diào)用函數(shù)中產(chǎn)生panic。
- 編譯器會把 defer 語句翻譯成對 deferproc 函數(shù)的調(diào)用,同時(shí),編譯器也會在使用了 defer 語句的 go 函數(shù)的末尾插入對 deferreturn 函數(shù)的調(diào)用。
- 每一個(gè)goroutine結(jié)構(gòu)體中都有一個(gè)_defer 指針變量用來存放defer單鏈表。defer保存用什么數(shù)據(jù)結(jié)構(gòu)?回答棧過不了面試官那關(guān),defer單鏈表應(yīng)該能過關(guān)。_defer 結(jié)構(gòu)體如下:
- siz:所有傳入?yún)?shù)的總大小。
- started:該 defer 是否已經(jīng)執(zhí)行過。
- heap:表明該defer是否存儲在heap上。
- sp:函數(shù)棧指針寄存器,一般指向當(dāng)前函數(shù)棧的棧頂。
- pc:程序計(jì)數(shù)器,有時(shí)稱為指令指針(IP),線程利用它來跟蹤下一個(gè)要執(zhí)行的指令。在大多數(shù)處理器中,PC指向的是下一條指令,而不是當(dāng)前指令。
- fn:指向傳入的函數(shù)地址和參數(shù)。
- _panic:指向 _panic 鏈表。
- link:指向 _defer 鏈表。
- 為什么defer要按照定義的順序逆序執(zhí)行:后面定義的函數(shù)可能會依賴前面的資源,所以要先執(zhí)行。如果前面先執(zhí)行,釋放掉這個(gè)依賴,那后面的函數(shù)就不能找到它的依賴了。
- defer函數(shù)定義時(shí),對外部變量的引用方式有兩種,分別是函數(shù)參數(shù)以及作為閉包引用。在作為函數(shù)參數(shù)的時(shí)候,在defer定義時(shí)就把值傳遞給defer,并被cache起來。如果是作為閉包引用,則會在defer真正調(diào)用的時(shí)候,根據(jù)整個(gè)上下文云確定當(dāng)前的值。
- defer后面的語句在執(zhí)行的時(shí)候,函數(shù)調(diào)用的參數(shù)會被保存起來,也就是復(fù)制一份。在真正執(zhí)行的時(shí)候,實(shí)際上用到的是復(fù)制的變量,也就是說,如果這個(gè)變量是一個(gè)“值類型”,那他就和定義的時(shí)候是一致的,如果是一個(gè)“引用”,那么就可能和定義的時(shí)候的值不一致
- 利用defer原理
- 利用defer先求值,再延遲調(diào)用的性質(zhì)
- defer與return
- defer語句的參數(shù)
- defer語句表達(dá)式的值在定義的時(shí)候就已經(jīng)確定了
- 閉包:由函數(shù)以及相關(guān)引用環(huán)境組合而成的實(shí)例,也就是說閉包=函數(shù)+引用環(huán)境
- 匿名函數(shù):匿名函數(shù)就是我們說的閉包,它不能獨(dú)立存在,但可以直接調(diào)用或者賦值于某個(gè)變量。一個(gè)閉包,繼承了函數(shù)聲明時(shí)的作用域。在go語言中,所有的匿名函數(shù)都是閉包
- 什么是defer
- defer配合recover
- recover:異常捕獲,可以讓程序在引發(fā)panic的時(shí)候不會崩潰退出。在引發(fā)panic的時(shí)候,panic會停掉當(dāng)前正在執(zhí)行的程序,但是,在這之前,它會有序的執(zhí)行完當(dāng)前goroutine的defer列表的語句。所以我們通常在defer里面掛一個(gè)recover,防止程序直接掛掉,類似于try...catch.recover()函數(shù)只在defer的上下文中才有效,直接調(diào)用,會返回nil