日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Golang面試必問——內(nèi)存逃逸分析

 菌心說 2022-01-16

這個題是小編面試遇到次數(shù)最多的題目之一了。在開始之前,我們先思考以下幾個問題,當(dāng)然,后面小編也會一一解答。

1,什么是內(nèi)存逃逸。

2,內(nèi)存逃逸的場景有哪些。

3,分析內(nèi)存逃逸的意義。

4,怎么避免內(nèi)存逃逸。


什么是內(nèi)存逃逸

在了解什么是內(nèi)存逃逸之前,我們先來簡單地熟悉一下兩個概念。棧內(nèi)存堆內(nèi)存。本次主要是講述的是Golang的內(nèi)存逃逸,故而關(guān)于內(nèi)存分配和垃圾回收就不做贅述了。后面小編會單獨(dú)出兩篇來寫這個,有需要的同學(xué)可以關(guān)注小編。關(guān)于這一塊,我們現(xiàn)在只需要了解三點(diǎn)。

  1. Golang的GC主要是針對堆的,不是棧。
  2. 引用類型的全局變量分配在堆上,值類型的全局變量分配在棧上。
  3. 局部變量內(nèi)存分配可能在棧上也可能在堆上。

有了前面的基礎(chǔ)知識,那我們簡單粗暴地介紹一下內(nèi)存逃逸。一個對象本應(yīng)該分配在棧上面,結(jié)果分配在了堆上面,這就是內(nèi)存逃逸。如下

文章圖片1

內(nèi)存逃逸的場景有哪些

要了解內(nèi)存逃逸的場景,首先我們要學(xué)會怎么分析內(nèi)存逃逸。其實(shí)分析起來很簡單,只需要一條簡單的命令,即gcflags。這個是有很多參數(shù)的,此處只舉一個最基本的例子。

go build -gcflags '-m' main.go

接下來我們就來討論一下內(nèi)存逃逸的場景有哪些。常見的場景有四種,小編總結(jié)為:局部指針返回,棧空間不足,動態(tài)類型,閉包引用。


局部指針返回

當(dāng)我們在某個方法內(nèi)定義了一個局部指針,并且將這個指針作為返回值返回時(shí),此時(shí)就發(fā)生了逃逸。這種類型的逃逸是比較常見的,如下。

文章圖片2
package mainimport (  'fmt')func main() {  str := returnPointer()  fmt.Println(*str)}// 返回局部指針func returnPointer() *string {  str := '更多免費(fèi)資料,關(guān)注公眾號:不穿格子衫的程序猿'  return &str}

??臻g不足

眾所周知,在系統(tǒng)中棧空間相比與總的內(nèi)存來說是非常小的。如下,小編的Mac是16G*512G的,可是整個系統(tǒng)中棧空間大小也才8M。

文章圖片3

而在我們的實(shí)際編碼過程中,大部分Goroutine的占用空間不到10KB(這也是Golang能支持高并發(fā)的原因之一)。而其中分配給棧的更是少之又少。所以一旦某個對象體積過大時(shí)候就會發(fā)生逃逸,從棧上面轉(zhuǎn)到堆上面。

如下,有兩個map,space1和space2,space1長度大小都是100,space2長度大小都是10000,結(jié)果space2發(fā)生了逃逸,space1沒有。

文章圖片4
package mainimport ( 'fmt')func main() { space() fmt.Println('更多免費(fèi)資料,關(guān)注公眾號:不穿格子衫的程序猿')}// 棧空間溢出func space() { // 不溢出 space1 := make([]int, 100, 100) for i := 0; i < len(space1); i++ { space1[i] = i } // 溢出 space2 := make([]int, 10000, 10000) for i := 0; i < len(space2); i++ { space2[i] = i }}

動態(tài)類型

小編認(rèn)為,這種內(nèi)存逃逸應(yīng)該是最多的,最常見的,而且還無法避免。簡單地說就是被調(diào)用函數(shù)的入?yún)⑹莍nterface或者是不定參數(shù),此時(shí)就會發(fā)生內(nèi)存逃逸。如下:

文章圖片5
package mainimport (  'fmt')func main() {  fmt.Println('關(guān)注公眾號:不穿格子衫的程序猿')}

哈哈哈,同學(xué)們是不是大跌眼鏡,一個簡簡單單的Println居然也會發(fā)生內(nèi)存逃逸。那么問題來了,這個是怎么導(dǎo)致的呢,廢話不多說,直接拔掉底褲擼源碼。此處就是所謂的動態(tài)類型。

文章圖片6

閉包調(diào)用

首先說一下,這種場景是非常少的,一般沒有人寫這種可讀性這么差的代碼,小編這串代碼都是參考別人的。所以小編認(rèn)為,這種場景,我們只需要知道即可,大概率是碰不上的。

文章圖片7
package mainimport ( 'fmt')func main() { fmt.Println(closure())}// 閉包逃逸func closure() func() string { return func() string { return '更多免費(fèi)資料,關(guān)注公眾號:不穿格子衫的程序猿' }}

分析內(nèi)存逃逸的意義

前面給大家列舉了四種內(nèi)存逃逸的場景,那么問題來了,分析內(nèi)存逃逸有什么用呢?簡單的總結(jié)就是兩點(diǎn):減輕GC的壓力,提高分配速度。

上文已經(jīng)說過,Golang的GC主要是針對堆的,而不是棧。試想一下,如果大量的對象從棧逃逸到堆上,是不是就會增加GC的壓力。在GC的過程中會占用比較大的系統(tǒng)開銷(一般可達(dá)到CPU容量的25%)。而且目前所有的GC都有STW這個死結(jié),而STW會造成用戶直觀的'卡頓'。非常影響用戶體驗(yàn)。

此外,堆和棧相比,堆適合不可預(yù)知大小的內(nèi)存分配。但是為此付出的代價(jià)是分配速度較慢,而且會形成內(nèi)存碎片。棧內(nèi)存分配則會非???。棧分配內(nèi)存只需要兩個CPU指令:“PUSH”和“RELEASE”,分配和釋放;而堆內(nèi)存分配首先需要去找到一塊大小合適的內(nèi)存塊,之后要通過垃圾回收才能釋放。

通過逃逸分析,可以盡量把那些不需要分配到堆上的變量直接分配到棧上,堆上的變量少了,會減輕分配堆內(nèi)存的開銷,同時(shí)也會減少GC的壓力,提高程序的運(yùn)行速度。


怎么避免內(nèi)存逃逸

最后說一下怎么避免內(nèi)存逃逸吧。首先需要注意的是,Golang在編譯的時(shí)候就可以確立逃逸,并不需要等到運(yùn)行時(shí)。這樣就給了咱們避免內(nèi)存逃逸的機(jī)會。

首先咱們明確一點(diǎn),小編認(rèn)為沒有任何方式能絕對避免內(nèi)存逃逸。原因嘛,就是存在【動態(tài)類型】這種逃逸方式,幾乎所有的庫函數(shù)都是動態(tài)類型的。當(dāng)然也不是說咱么要破罐子破摔,該避免還是要避免一下的,主要的原則有以下幾種,分別針對上面幾種場景。

  1. 盡量減少外部指針引用,必要的時(shí)候可以使用值傳遞。
  2. 對于自己定義的數(shù)據(jù)大小,有一個基本的預(yù)判,盡量不要出現(xiàn)棧空間溢出的情況。
  3. Golang中的接口類型的方法調(diào)用是動態(tài)調(diào)度,如果對于性能要求比較高且訪問頻次比較高的函數(shù)調(diào)用,應(yīng)該盡量避免使用接口類型。
  4. 盡量不要寫閉包函數(shù),可讀性差還逃逸。

資料環(huán)節(jié)

又到了大家期待的福利時(shí)間了。本次贈送的是Golang實(shí)戰(zhàn)案例20份。

文章圖片8

廢話不多說,各位看官大人要怎么獲取呢。很簡單,關(guān)注小編,私信資料」即可獲得免費(fèi)獲取方式。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多