詳解Go語言中make和new的區(qū)別
寫在前面
雖然 make 和 new 都是能夠用于初始化數(shù)據(jù)結(jié)構(gòu),但是它們兩者能夠初始化的結(jié)構(gòu)類型卻有著較大的不同;make 在 Go 語言中只能用于初始化語言中的3種類型:slice、map、chan
slice := make([]int, 0, 100) hash := make(map[int]bool, 10) ch := make(chan int, 5)
這些基本類型都是語言為我們提供的,我們在前面的章節(jié)中其實(shí)已經(jīng)介紹過了它們初始化的過程以及原理,但是在這里還是需要提醒各位讀者注意的是,這三者返回了不同類型的數(shù)據(jù)結(jié)構(gòu):
slice是一個(gè)包含data、cap和len的結(jié)構(gòu)體;hash是一個(gè)指向hmap結(jié)構(gòu)體的指針;ch是一個(gè)指向hchan結(jié)構(gòu)體的指針;
而另一個(gè)用于初始化數(shù)據(jù)結(jié)構(gòu)的關(guān)鍵字 new 的作用其實(shí)就非常簡單了,它只是接收一個(gè)類型作為參數(shù)然后返回一個(gè)指向這個(gè)類型的指針:
i := new(int) var v int i := &v
上述代碼片段中的兩種不同初始化方法其實(shí)是等價(jià)的,它們都會(huì)創(chuàng)建一個(gè)指向 int 零值的指針。
到了這里我們對 Go 語言中這兩種不同關(guān)鍵字的使用也有了一定的了解:make 用于創(chuàng)建切片、哈希表和管道等內(nèi)置數(shù)據(jù)結(jié)構(gòu),new 用于分配并創(chuàng)建一個(gè)指向?qū)?yīng)類型的指針。
實(shí)現(xiàn)原理
接下來我們將分別介紹 make 和 new 在初始化不同數(shù)據(jù)結(jié)構(gòu)時(shí)的具體過程,我們會(huì)從編譯期間和運(yùn)行時(shí)兩個(gè)不同的階段理解這兩個(gè)關(guān)鍵字的原理,不過由于前面已經(jīng)詳細(xì)地介紹過 make 的實(shí)現(xiàn)原理,所以我們會(huì)將重點(diǎn)放在 new 上從 Go 語言的源代碼層面分析它的實(shí)現(xiàn)。
make
在前面的章節(jié)中我們其實(shí)已經(jīng)談到過 make 在創(chuàng)建 數(shù)組和切片、哈希表 和 Channel 的具體過程,所以在這一小節(jié)中,我們也只是會(huì)簡單提及 make 相關(guān)的數(shù)據(jù)結(jié)構(gòu)初始化原理。
在編譯期間的 類型檢查 階段,Go 語言其實(shí)就將代表 make 關(guān)鍵字的 OMAKE 節(jié)點(diǎn)根據(jù)參數(shù)類型的不同轉(zhuǎn)換成了 OMAKESLICE、OMAKEMAP 和 OMAKECHAN 三種不同類型的節(jié)點(diǎn),這些節(jié)點(diǎn)最終也會(huì)調(diào)用不同的運(yùn)行時(shí)函數(shù)來初始化數(shù)據(jù)結(jié)構(gòu)。
new
內(nèi)置函數(shù) new 會(huì)在編譯期間的 SSA 代碼生成 階段經(jīng)過 callnew 函數(shù)的處理,如果請求創(chuàng)建的類型大小時(shí) 0,那么就會(huì)返回一個(gè)表示空指針的 zerobase 變量,在遇到其他情況時(shí)會(huì)將關(guān)鍵字轉(zhuǎn)換成 newobject:
func callnew(t *types.Type) *Node {
if t.NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", t)
}
dowidth(t)
if t.Size() == 0 {
z := newname(Runtimepkg.Lookup("zerobase"))
z.SetClass(PEXTERN)
z.Type = t
return typecheck(nod(OADDR, z, nil), ctxExpr)
}
fn := syslook("newobject")
fn = substArgTypes(fn, t)
v := mkcall1(fn, types.NewPtr(t), nil, typename(t))
v.SetNonNil(true)
return v
}需要提到的是,哪怕當(dāng)前變量是使用 var 進(jìn)行初始化,在這一階段可能會(huì)被轉(zhuǎn)換成 newobject 的函數(shù)調(diào)用并在堆上申請內(nèi)存:
func walkstmt(n *Node) *Node {
switch n.Op {
case ODCL:
v := n.Left
if v.Class() == PAUTOHEAP {
if prealloc[v] == nil {
prealloc[v] = callnew(v.Type)
}
nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v])
nn.SetColas(true)
nn = typecheck(nn, ctxStmt)
return walkstmt(nn)
}
case ONEW:
if n.Esc == EscNone {
r := temp(n.Type.Elem())
r = nod(OAS, r, nil)
r = typecheck(r, ctxStmt)
init.Append(r)
r = nod(OADDR, r.Left, nil)
r = typecheck(r, ctxExpr)
n = r
} else {
n = callnew(n.Type.Elem())
}
}
}當(dāng)然這也不是絕對的,如果當(dāng)前聲明的變量或者參數(shù)不需要在當(dāng)前作用域外『生存』,那么其實(shí)就不會(huì)被初始化在堆上,而是會(huì)初始化在當(dāng)前函數(shù)的棧中并隨著 函數(shù)調(diào)用 的結(jié)束而被銷毀。
newobject 函數(shù)的工作就是獲取傳入類型的大小并調(diào)用 mallocgc 在堆上申請一片大小合適的內(nèi)存空間并返回指向這片內(nèi)存空間的指針:
func newobject(typ *_type) unsafe.Pointer {
return mallocgc(typ.size, typ, true)
}mallocgc 函數(shù)的實(shí)現(xiàn)大概有 200 多行代碼,在這一節(jié)中就不展開詳細(xì)分析了,我們會(huì)在后面的章節(jié)中詳細(xì)介紹 Go 語言的內(nèi)存管理機(jī)制。
總結(jié)
到了最后,簡單總結(jié)一下 Go 語言中 make 和 new 關(guān)鍵字的實(shí)現(xiàn)原理,make 關(guān)鍵字的主要作用是創(chuàng)建切片、哈希表和 Channel 等內(nèi)置的數(shù)據(jù)結(jié)構(gòu),而 new 的主要作用是為類型申請一片內(nèi)存空間,并返回指向這片內(nèi)存的指針。
參考
[1]大神是如何學(xué)習(xí) Go 語言之 make 和 new
到此這篇關(guān)于詳解Go語言中make和new的區(qū)別的文章就介紹到這了,更多相關(guān)Go語言make new內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang?metrics各個(gè)指標(biāo)含義講解說明
這篇文章主要為大家介紹了golang?metrics各個(gè)指標(biāo)含義講解說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
go?sync.Once實(shí)現(xiàn)高效單例模式詳解
這篇文章主要為大家介紹了go?sync.Once實(shí)現(xiàn)高效單例模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Go中字符串處理?fmt.Sprintf與string.Builder的區(qū)別對比分析
在Go語言中,我們通常會(huì)遇到兩種主要的方式來處理和操作字符串:使用fmt.Sprintf函數(shù)和string.Builder類型,本文給大家介紹它們在性能和用法上有一些關(guān)鍵區(qū)別,感興趣的朋友跟隨小編一起看看吧2023-11-11
golang 生成二維碼海報(bào)的實(shí)現(xiàn)代碼
這篇文章主要介紹了golang 生成二維碼海報(bào)的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
GO比較兩個(gè)對象是否相同實(shí)戰(zhàn)案例
我們時(shí)常有比較兩個(gè)值是否相等的需求,下面這篇文章主要給大家介紹了關(guān)于GO比較兩個(gè)對象是否相同的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
Go?模塊在下游服務(wù)抖動(dòng)恢復(fù)后CPU占用無法恢復(fù)原因
這篇文章主要為大家介紹了Go?模塊在下游服務(wù)抖動(dòng)恢復(fù)后CPU占用無法恢復(fù)原因詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

