【Go內(nèi)存分配】

程序的運行都需要內(nèi)存,比如變量的創(chuàng)建蔚出、函數(shù)的調(diào)用询刹、數(shù)據(jù)的計算等。所以在需要內(nèi)存的時候就需要申請內(nèi)存湘纵,進(jìn)行內(nèi)存分配。在C/C++這類語言中滤淳,內(nèi)存是由開發(fā)者自己管理的梧喷,需要主動申請和釋放,而在Go語言中則是由該語言自己管理的脖咐,開發(fā)者不用關(guān)心太多铺敌,只需要聲明變量,Go語言就會根據(jù)變量的類型自動分配相應(yīng)的內(nèi)存屁擅。

Go語言程序所管理的虛擬內(nèi)存空間被分為兩個部分:**堆內(nèi)存和棧內(nèi)存**偿凭。棧內(nèi)存主要有Go語言來管理,開發(fā)者無法干涉太多派歌,堆內(nèi)存才是我們開發(fā)者的舞臺笔喉,因為程序的數(shù)據(jù)大部分分配在堆內(nèi)存上,一個程序的大部分內(nèi)存占用也是在堆內(nèi)存上硝皂。

提示: 我們常說的Go語言的內(nèi)存垃圾回收是針對堆內(nèi)存的垃圾回收常挚。

變量的聲明、初始化就涉及內(nèi)存的分配稽物,比如聲明變量會用到var關(guān)鍵字奄毡,如果要對變量初始化,就會用到 = 賦值運算符贝或、除此之外還可以使用內(nèi)置函數(shù)make和new吼过,這兩個函數(shù)功能非常相似,但是可能還會有些迷惑咪奖,接下來讓我們基于內(nèi)存分配盗忱,引出內(nèi)置函數(shù)make和new,講講它們的不同羊赵,以及使用場景趟佃。

變量

一個數(shù)據(jù)類型,在聲明初始化后都會被賦值給一個變量昧捷,變量存儲了程序運行所需的數(shù)據(jù)闲昭。

變量的聲明

// 我們來復(fù)習(xí)一下變量的聲明
var s string 
// 這個例子中,我們只是聲明了一個變量s靡挥,類型為string序矩,并沒有對它進(jìn)行初始化,所以它的值是string的零值跋破,也就是 ""(空字符串)

var sp *string
// 我們聲明了一個指針類型的變量簸淀,也沒有初始化瓶蝴,所以它的值是*string的零值,即nil

變量的賦值

變量可以通過=賦值運算符租幕,修改變量的值舷手。如果在聲明一個變量的時候就給這個變量賦值,這種操作稱為變量的初始化令蛉。如果要對一個變量初始化聚霜,可以有三種辦法
  1. 聲明時直接初始化,var s string = "奔跑的蝸牛"
  2. 聲明后在初始化珠叔,s = "奔跑的蝸牛" // (假設(shè)已經(jīng)聲明了變量s)
  3. 使用簡短聲明蝎宇,如 s:= "奔跑的蝸牛"

提示:變量的初始化也是一種賦值,只不過發(fā)生在變量聲明的時候祷安,時機(jī)最靠前姥芥。即獲得這個變量時,就已經(jīng)被賦值了

那么汇鞭,指針類型的變量能直接賦值嗎凉唐?

func main() {
    var sp *string
    *sp = "奔跑的蝸牛"
    fmt.Println(*sp)
}

// 上述代碼運行會報錯,錯誤信息如下:
painc: runtime error: invalid memory address or nil pointer dereference


// 因為指針類型的變量如果沒有分配內(nèi)存霍骄,就默認(rèn)值是nil台囱,它沒有指向的內(nèi)存,所以無法師用读整,強(qiáng)行使用會得到 nil指針錯誤簿训。  對于值類型來說,即使只是聲明一個變量米间,并沒有對其初始化强品,該變量也會有分配好的內(nèi)存。

func main() {
    var s string
    fmt.Println(&s)
}
// 我們可以獲取到變量s的內(nèi)存地址屈糊,這是Go語言幫我們做的的榛,可以直接使用。

var wg sync.WaitGroup 聲明的變量wg逻锐,我們并沒有對其初始化就可以使用了夫晌,因為sync.WaitGroup 是一個 struct 結(jié)構(gòu)體,是一個值類型谦去,Go語言自動分配了內(nèi)存慷丽,所以可以直接使用,不會報nil異常鳄哭。

小結(jié)如果要對一個變量賦值,這個變量必須有對應(yīng)的分配好的內(nèi)存纲熏,這樣才可以對這塊內(nèi)存操作妆丘,完成賦值操作锄俄。對于指針變量,如果沒有分配內(nèi)存勺拣,取值操作也一樣會報nil異常奶赠,因為沒有可以操作的內(nèi)存。 所以药有,一個變量必須要經(jīng)過聲明毅戈、內(nèi)存分配才能賦值,才可以在聲明的時候進(jìn)行初始化愤惰。指針類型在聲明的時候苇经,Go語言沒有自動分配好內(nèi)存,所以不能對其進(jìn)行賦值操作宦言,這一點和值類型不一樣扇单。

提示: map和chan也一樣,它們本質(zhì)上也是指針類型奠旺。

new函數(shù)

我們知道了聲明的指針變量是沒有分配內(nèi)存的蜘澜,那么如何給它分配一塊呢? 那就是通過**內(nèi)置new函數(shù)**响疚。
func main() {
    var sp *string
    sp = new(string) // 關(guān)鍵點鄙信,通過內(nèi)置new函數(shù)生成了一個*string,并賦值給了變量sp忿晕。
    *sp = "奔跑的蝸牛"
    fmt.Println(*sp)
}

// 內(nèi)置new的作用是什么呢装诡? 我們來分析一下源碼
func new(Type) *Type
**new函數(shù)**的作用就是根據(jù)傳入的類型申請一塊內(nèi)存,然后返回指向這塊內(nèi)存的指針杏糙,指針指向的數(shù)據(jù)就是該類型的零值慎王。比如傳入的類型是string,那么返回的就是string指針宏侍,這個string指針指向的數(shù)據(jù)就是空字符串赖淤。
spl = new(string)
fmt.Println(*spl) // 打印空字符串,也就是string的零值谅河。
通過new函數(shù)分配內(nèi)存并返回指向該內(nèi)存的指針后咱旱,就可以通過該指針對這塊內(nèi)存進(jìn)行賦值、取值等操作绷耍。

變量初始化

當(dāng)聲明了一些類型的變量時吐限,這些變量的零值并不能滿足我們需要,這時需要在變量聲明同時進(jìn)行賦值(修改變量的值)褂始,這個過程稱為變量初始化诸典。

不止基礎(chǔ)類型可以通過字面量的方式進(jìn)行初始化,復(fù)合類型也可以崎苗,比如結(jié)構(gòu)體狐粱。
type person struct{
    name string
    age int
}

func main() {
    // 字面量初始化
    p := person{name:"zhangsan", age:18}
}

指針變量初始化

我們知道new函數(shù)可以申請內(nèi)存并返回一個指向該內(nèi)存的指針舀寓,但是這塊內(nèi)存中數(shù)據(jù)的默認(rèn)值是該類型的零值,在一些情況下并不滿足需要肌蜻。如果我們想要獲得一個*person類型的指針互墓,并且初始化,但是new函數(shù)只有一個類型參數(shù)蒋搜,并沒有初始化值的參數(shù)篡撵,怎么辦呢? 可以自定義一個函數(shù)豆挽,對指針變量進(jìn)行初始化育谬。
func NewPerson() *person {
    p := new(person)
    p.name = "zhangsan"
    p.age = 18
    return p
}

p := NewPerson()
fmt.Println("name:", p.name, "age:", p.age)

// 這就是工廠函數(shù),NewPerson 函數(shù)就是工廠函數(shù)祷杈,除了使用new函數(shù)創(chuàng)建了指針外斑司,還進(jìn)行了賦值,即初始化但汞。通過NewPerson函數(shù)做了一層包裝宿刮,把內(nèi)存分配(new 函數(shù))和初始化(賦值)都完成了。

make函數(shù)

接下來講講make函數(shù)私蕾。我們知道僵缺,使用make函數(shù)創(chuàng)建map的時候,其實調(diào)用的是makehmap函數(shù)踩叭。
func makemap(t *maptype, hint int ,h *hmap) *hmap {}

// makemap函數(shù)返回的是*hmap類型磕潮,而hmap是一個結(jié)構(gòu)體,源碼如下

type hmap struct {
    count       int 
    flags       uint8
    B           uint8
    noverflow   uint16
    hash0       uint16
    buckets     unsafe.Pointer
    oldbuckets  unsafe.Pointer
    nevacuate   uintptr
    extra       *mapextra
}


m := make(map[string]int, 10)
// make函數(shù)和我們的自定義NewPerson函數(shù)很像容贝。其實make函數(shù)就是map類型的工廠函數(shù)自脯,它可以根據(jù)傳遞它的K-V鍵值對類型,創(chuàng)建不同類型的map斤富,同時可以初始化map的大小膏潮。
可以看到,我們平常使用的map關(guān)鍵字非常復(fù)雜满力,包含map的大小count焕参、存儲桶buckets等。要想這樣使用hmap油额,不是簡單的通過new函數(shù)返回一個*hmap就可以叠纷,還需要對其初始化,這就是make函數(shù)要做的事情潦嘶。

提示: make函數(shù)不只是map類型的工廠函數(shù)涩嚣,還是chan、slice的工廠函數(shù)。它同時可以用于slice缓艳、chan和map三種類型的初始化校摩。

總結(jié):new和make函數(shù)的區(qū)別如下:

1. new函數(shù)只用于分配內(nèi)存看峻,并且把內(nèi)存清零阶淘,也就是返回一個指向?qū)?yīng)類型零值的指針。new函數(shù)一般用于需要顯示的返回指針的情況互妓,不是太常用溪窒。**new返回的是對象的指針,對指針?biāo)趯ο蟮母姆朊悖瑫绊懼羔樦赶虻脑紝ο蟮闹?*澈蚌。
2. make函數(shù)只用于slice、chan和map這三種內(nèi)置類型的創(chuàng)建和初始化灼狰,因為這三種類型的結(jié)構(gòu)比較復(fù)雜宛瞄,比如slice要提前初始化號內(nèi)部元素的類型、slice的長度和容量等交胚,這樣才能更好地使用它們份汗。**make返回的是對象**
 *   對值類型對象的修改,不會影響原始對象的值
 *   對引用類型的修改蝴簇,會影響原始對象的值

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杯活,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子熬词,更是在濱河造成了極大的恐慌旁钧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件互拾,死亡現(xiàn)場離奇詭異歪今,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)颜矿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門寄猩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人或衡,你說我怎么就攤上這事焦影。” “怎么了封断?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵斯辰,是天一觀的道長。 經(jīng)常有香客問我坡疼,道長彬呻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮闸氮,結(jié)果婚禮上剪况,老公的妹妹穿的比我還像新娘。我一直安慰自己蒲跨,他們只是感情好译断,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著或悲,像睡著了一般孙咪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巡语,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天翎蹈,我揣著相機(jī)與錄音,去河邊找鬼男公。 笑死荤堪,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的枢赔。 我是一名探鬼主播澄阳,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼糠爬!你這毒婦竟也來了寇荧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤执隧,失蹤者是張志新(化名)和其女友劉穎揩抡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镀琉,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡峦嗤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了屋摔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烁设。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖钓试,靈堂內(nèi)的尸體忽然破棺而出装黑,到底是詐尸還是另有隱情,我是刑警寧澤弓熏,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布恋谭,位于F島的核電站,受9級特大地震影響挽鞠,放射性物質(zhì)發(fā)生泄漏疚颊。R本人自食惡果不足惜狈孔,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望材义。 院中可真熱鬧均抽,春花似錦、人聲如沸其掂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽清寇。三九已至喘漏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間华烟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工持灰, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留盔夜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓堤魁,卻偏偏與公主長得像喂链,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子妥泉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容