日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

深入淺出內(nèi)存管理:空間分配及逃逸分析

來源: 責編: 時間:2023-12-22 09:36:34 241觀看
導讀1. 引言內(nèi)存管理,是開發(fā)者在程序編寫和調(diào)優(yōu)的過程中不可繞開的話題,也是走向資深程序員必須要了解的計算機知識。有經(jīng)驗的面試官會從內(nèi)存管理的掌握程度去考察一個候選人的技術水平,這里面涉及到的知識可能包括操作系統(tǒng)

1. 引言

內(nèi)存管理,是開發(fā)者在程序編寫和調(diào)優(yōu)的過程中不可繞開的話題,也是走向資深程序員必須要了解的計算機知識。d6f28資訊網(wǎng)——每日最新資訊28at.com

有經(jīng)驗的面試官會從內(nèi)存管理的掌握程度去考察一個候選人的技術水平,這里面涉及到的知識可能包括操作系統(tǒng)、計算機組成原理以及編程語言的底層實現(xiàn)等。d6f28資訊網(wǎng)——每日最新資訊28at.com

說到內(nèi)存,其實就是存儲器,我們可以從馮.諾依曼的計算機結(jié)構(gòu)來了解存儲器的概念:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

什么?馮諾依曼你都不知道,是不是和我一樣,計算機基礎的課程沒有好好學呀?d6f28資訊網(wǎng)——每日最新資訊28at.com

別急!接下來我們由淺入深講到的內(nèi)容,就算不了解計算機底層原理的同學也可以弄懂,一起接著往下看吧~d6f28資訊網(wǎng)——每日最新資訊28at.com

總之,存儲器是計算機中不可或缺的一部分,內(nèi)存管理,其實就是對存儲器的存儲空間管理。d6f28資訊網(wǎng)——每日最新資訊28at.com

接下來,我們會從內(nèi)存分類、以及 Go 語言的內(nèi)存空間分配上,結(jié)合常見的逃逸分析場景,來學習內(nèi)存管理相關的知識。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

2. 虛擬內(nèi)存

2.1 虛擬內(nèi)存和物理內(nèi)存的區(qū)別

我們都知道,以前的計算機存儲器空間很小,我們在運行計算機程序的時候物理尋址的范圍非常有限。d6f28資訊網(wǎng)——每日最新資訊28at.com

比如,在 32 位的機器上,尋址范圍只有 2 的 32 次方,也就是 4G。d6f28資訊網(wǎng)——每日最新資訊28at.com

并且,對于程序來說,這是固定的,我們可以想象一下,如果每開一個計算機進程就給它們分配 4G 的物理內(nèi)存,那資源消耗就太大了。d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

資源的利用率也是一個巨大的問題,沒有分配到資源的進程就只能等待,當一個進程結(jié)束以后再把等待的進程裝入內(nèi)存,而這種頻繁地裝入內(nèi)存操作效率也很低。d6f28資訊網(wǎng)——每日最新資訊28at.com

并且,由于指令都是可以訪問物理內(nèi)存的,那么任何進程都可以修改內(nèi)存中其它進程的數(shù)據(jù),甚至修改內(nèi)核地址空間的數(shù)據(jù),這是非常不安全的。d6f28資訊網(wǎng)——每日最新資訊28at.com

由于物理內(nèi)存使用時,資源消耗大、利用率低及不安全的問題。因此,引入了虛擬內(nèi)存。d6f28資訊網(wǎng)——每日最新資訊28at.com

虛擬內(nèi)存是計算機系統(tǒng)內(nèi)存管理的一種技術,通過分配虛擬的邏輯內(nèi)存地址,讓每個應用程序都認為自己擁有連續(xù)可用的內(nèi)存空間。d6f28資訊網(wǎng)——每日最新資訊28at.com

而實際上,這些內(nèi)存空間通常是被分隔開的多個物理內(nèi)存碎片,還有部分暫時存儲在外部磁盤存儲器上,在需要時進行數(shù)據(jù)交換。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

2.2 虛擬內(nèi)存轉(zhuǎn)換

既然計算機用到的都是虛擬內(nèi)存,那我們?nèi)绾文玫秸鎸嵉奈锢韮?nèi)存地址呢?答案就是內(nèi)存映射,即如何把虛擬地址(又被稱作邏輯地址)轉(zhuǎn)換成物理地址。d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

在 Linux 操作系統(tǒng)下,內(nèi)存最先有兩種管理方式,分別是頁式存儲管理和段式存儲管理,其中:d6f28資訊網(wǎng)——每日最新資訊28at.com

  • 頁式存儲能有效地解決內(nèi)存碎片,提高內(nèi)存利用率;
  • 分段式存儲管理能反映程序的邏輯結(jié)構(gòu),并有利于段的共享;

通俗來講就是內(nèi)存有兩種單位,一種是分頁,一種是分段。分頁就是把整個虛擬和物理內(nèi)存空間切割成很多塊固定尺寸的大小,虛擬地址和物理地址間通過頁表來進行映射:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

分頁內(nèi)存都是預先劃分好的,所以不會產(chǎn)生間隙非常小的內(nèi)存碎片,分配時利用率比較高。d6f28資訊網(wǎng)——每日最新資訊28at.com

而分段就不一樣了,它是基于程序的邏輯來分段的,由于程序?qū)傩钥赡艽蟛幌嗤苑侄蔚拇笮∫矔笮〔灰弧?span style="display:none">d6f28資訊網(wǎng)——每日最新資訊28at.com

分段管理時,虛擬地址和物理地址間通過段表來進行映射:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

不難發(fā)現(xiàn),分段內(nèi)存管理的切分不是均勻的,而是根據(jù)不同的程序所占用的內(nèi)存來分配。d6f28資訊網(wǎng)——每日最新資訊28at.com

這樣帶來的問題是,假設程序1的內(nèi)存(1G)用完了釋放后,另一個程序4(假設內(nèi)存需要1000M)裝到物理內(nèi)存中可能還剩余 24M 內(nèi)存,如果系統(tǒng)中有大量的這種內(nèi)存碎片,那整體的內(nèi)存利用率就會很低。d6f28資訊網(wǎng)——每日最新資訊28at.com

于是,段頁式內(nèi)存管理方式出現(xiàn)了,它將以上兩種存儲管理方法結(jié)合起來:即先把用戶程序分成若干個段,為每一個段分配一個段名,再把每個段分成若干個頁。d6f28資訊網(wǎng)——每日最新資訊28at.com

在段頁式系統(tǒng)中,為了實現(xiàn)從邏輯地址到物理地址的轉(zhuǎn)換,系統(tǒng)中需要同時配置段表和頁表,利用段表和頁表進行從用戶地址到物理內(nèi)存空間的映射。d6f28資訊網(wǎng)——每日最新資訊28at.com

系統(tǒng)為每個進程創(chuàng)建一張段表,每個分段上有一個頁表。段表包括段號、頁表長度和頁表始址,頁表包含頁號和塊號。d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

在地址轉(zhuǎn)換時,首先通過段表查到頁表地址,再通過頁表獲取頁幀號,最終形成物理地址。d6f28資訊網(wǎng)——每日最新資訊28at.com

虛擬內(nèi)存到物理內(nèi)存的映射,是操作系統(tǒng)層面去管理的。而我們在開發(fā)時,涉及到的內(nèi)存管理,往往只是軟件程序去調(diào)用虛擬內(nèi)存時要做的工作:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

接下來,我們從虛擬內(nèi)存的構(gòu)成來分析下軟件開發(fā)中的內(nèi)存管理。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

3. 內(nèi)存管理

程序在虛擬內(nèi)存上被分為棧區(qū)、堆區(qū)、數(shù)據(jù)區(qū)、全局數(shù)據(jù)區(qū)、代碼段五個部分。d6f28資訊網(wǎng)——每日最新資訊28at.com

而內(nèi)存的管理,就是對內(nèi)存空間進行合理化使用,主要是堆區(qū)(Heap)和棧區(qū)(Stack)這兩個重要區(qū)域的分配使用。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

3.1 堆與棧

虛擬內(nèi)存里有兩塊比較重要的地址空間,分別為堆和棧空間。對于 C++ 等底層的編程語言,棧上的內(nèi)存空間由編譯器統(tǒng)一管理,而堆上的內(nèi)存空間需要程序員來手動管理進行分配和回收。d6f28資訊網(wǎng)——每日最新資訊28at.com

在 Go 語言中,棧上的內(nèi)存空間也是由編譯器來統(tǒng)一管理,而堆上的內(nèi)存空間由編譯器和垃圾收集器共同管理進行分配和回收,這給我們程序員帶來了極大的便利性。d6f28資訊網(wǎng)——每日最新資訊28at.com

在棧上分配和回收內(nèi)存的開銷很低,只需要 2 個指令:PUSH 和 POP。PUSH 將數(shù)據(jù)壓入棧中,POP 釋放空間,消耗的僅是將數(shù)據(jù)拷貝到內(nèi)存的時間。d6f28資訊網(wǎng)——每日最新資訊28at.com

而在堆上分配內(nèi)存時,不僅分配的時候慢,而且垃圾回收的時候也比較費勁,比如說 Go 在 1.8 以后就用到了三色標記法+混合寫屏障的技術來做垃圾回收。總體來看,堆內(nèi)存分配比棧內(nèi)存分配導致的開銷要大很多。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

3.2 堆棧內(nèi)存分配

1)內(nèi)存分配的挑戰(zhàn)

  • 像 C/C++ 這類由用戶程序申請內(nèi)存空間,可能會頻繁地進行內(nèi)存申請和回收,但每次內(nèi)存分配時都需要進行系統(tǒng)調(diào)用(即只有進入內(nèi)核態(tài)才可以申請內(nèi)存),就會導致系統(tǒng)的性能很低。
  • 除此之外,還可能會有多線程(Go語言里面也有協(xié)程)去訪問同一個地址空間的情況,這時就必定需要對內(nèi)存進行加鎖,帶來的開銷也會比較大。
  • 初始化時堆內(nèi)存是一整塊連續(xù)的內(nèi)存,但隨著系統(tǒng)運行過程中不斷申請回收內(nèi)存,可能會產(chǎn)生許多的內(nèi)存碎片,導致內(nèi)存的使用效率降低。

程序進行內(nèi)存分配時,為了應對以上最常見的三種問題,Go 語言結(jié)合谷歌的 TCMalloc(ThreadCacheMalloc) 內(nèi)存回收方法,做了一些改進。d6f28資訊網(wǎng)——每日最新資訊28at.com

同時,TCMalloc 和 Go 進行內(nèi)存分配時都會引入線程緩存(mcentral of P)、中心緩存(mcentral)和頁堆(mheap)三個組件進行分級管理內(nèi)存。如圖所示:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

線程緩存屬于每一個獨立的線程或協(xié)程,里面存儲了每個線程所用的內(nèi)存塊 span,由于內(nèi)存塊的大小不一,所以有上百個內(nèi)存塊類別 span class,這些內(nèi)存塊里面分別管理不同大小的內(nèi)存空間(比如 8KB、16KB、32KB...)。由于不涉及多線程,所以不需要使用互斥鎖來保護內(nèi)存,以減少鎖競爭帶來的性能損耗。d6f28資訊網(wǎng)——每日最新資訊28at.com

當線程緩存的空間不夠時,會使用中心緩存作為小對象內(nèi)存的分配,中心緩存和線程緩存的每個 span class 一一對應,并且中心緩存的每個 span class 中有兩個內(nèi)存塊,分別存儲了分配過內(nèi)存的空間和滿內(nèi)存空間,以提升內(nèi)存分配的效率。如果中心緩存還不滿足,就向頁堆進行空間申請。d6f28資訊網(wǎng)——每日最新資訊28at.com

為了提升空間的利用率,當遇到中大對象(>=32KB)分配時,內(nèi)存分配器會選擇頁堆直接進行分配。d6f28資訊網(wǎng)——每日最新資訊28at.com

Go 語言內(nèi)存分配的核心是使用多級緩存將對象根據(jù)大小分類,并按照類別來實施不同的分配策略。如上圖所示,應用程序在申請內(nèi)存時會根據(jù)對象的大小(Tiny 小對象或者 Large and medium 中大對象),向不同的組件去申請內(nèi)存空間。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

2)棧內(nèi)存分配

棧區(qū)的內(nèi)存一般由編譯器自動分配和釋放,一般來說,棧區(qū)存儲著函數(shù)入?yún)⒁约熬植孔兞浚@些數(shù)據(jù)會隨著函數(shù)的創(chuàng)建而創(chuàng)建,函數(shù)的返回而消亡,一般不會在程序中長期存在。d6f28資訊網(wǎng)——每日最新資訊28at.com

這種線性的內(nèi)存分配策略有著極高地效率,但是工程師也往往不能控制棧內(nèi)存的分配,這部分工作基本都是由編譯器完成的。d6f28資訊網(wǎng)——每日最新資訊28at.com

棧空間在運行時中包含兩個重要的全局變量,分別是 runtime.stackpool 和 runtime.stackLarge,這兩個變量分別表示全局的棧緩存和大棧緩存,前者可以分配小于 32KB 的內(nèi)存,后者用來分配大于 32KB 的棧空間:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

棧分配時,根據(jù)線程緩存和申請棧的大小,Go 語言會通過三種不同的方法分配棧空間:d6f28資訊網(wǎng)——每日最新資訊28at.com

  1. 如果棧空間較小,使用全局棧緩存或者線程緩存上固定大小的空閑鏈表分配內(nèi)存;
  2. 如果棧空間較大,從全局的大棧緩存 runtime.stackLarge 中獲取內(nèi)存空間;
  3. 如果棧空間較大并且 runtime.stackLarge 空間不足,在堆上申請一片大小足夠內(nèi)存空間。

在 Go1.4 以后,最小的棧內(nèi)存大小為 2KB,即一個 goroutine 協(xié)程的大小。所以,當程序里的協(xié)程數(shù)量超過棧內(nèi)存可分配的最大值后,就會分配在堆空間里面。也就是說,雖然 Go 語言里面可以用 go 關鍵字分配不限數(shù)量的 goroutine 協(xié)程,但是在性能上,我們分配的 goroutine 個數(shù)最好不要超過棧空間的最大值。d6f28資訊網(wǎng)——每日最新資訊28at.com

假設,棧內(nèi)存的最大值為 8MB,那分配的 goroutine 數(shù)量最好不要超過 4000 個(8MB/2KB)。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

4. 逃逸分析

4.1 Go如何做逃逸分析

在 C 語言和 C++ 這類需要手動管理內(nèi)存的編程語言中,將對象或者結(jié)構(gòu)體分配到棧上或者堆上是由工程師來決定的,這也為工程師的工作帶來的挑戰(zhàn):如何精準地為每一個變量分配合理的空間,提升整個程序的運行效率和內(nèi)存使用效率。但是 C 和 C++ 的這種手動分配內(nèi)存會導致如下的兩個問題:d6f28資訊網(wǎng)——每日最新資訊28at.com

  1. 不需要分配到堆上的對象分配到了堆上 — 浪費內(nèi)存空間;
  2. 需要分配到堆上的對象分配到了棧上 — 產(chǎn)生野指針、影響內(nèi)存安全;

與野指針相比,浪費內(nèi)存空間反而是小問題。在 C 語言中,棧上的變量被函數(shù)作為返回值返回給調(diào)用方是一個常見的錯誤,在如下所示的代碼中,棧上的變量 i 被錯誤返回:d6f28資訊網(wǎng)——每日最新資訊28at.com

int *dangling_pointer() {   int i = 2;   return &i;}

當 dangling_pointer 函數(shù)返回后,它的本地變量會被編譯器回收(棧上空間的機制),調(diào)用方獲取的是危險的野指針。如果程序里面出現(xiàn)大量不合法的指針值,在大型項目中是比較難以發(fā)現(xiàn)和定位的。d6f28資訊網(wǎng)——每日最新資訊28at.com

當所指向的對象被釋放或者收回,但是對該指針沒有作任何的修改,以至于該指針仍舊指向已經(jīng)回收的內(nèi)存地址,此情況下該指針便稱野指針,或稱懸空指針、迷途指針。——wiki百科d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

那么,在 Go 語言里面,編譯器該如何知道某個變量需要分配在堆,還是棧上而避免出現(xiàn)這種問題呢?d6f28資訊網(wǎng)——每日最新資訊28at.com

編譯器決定內(nèi)存分配位置的方式,就稱之為逃逸分析。逃逸分析由編譯器完成,作用于編譯階段。在編譯器優(yōu)化中,逃逸分析是用來決定指針動態(tài)作用域的方法。Go 語言的編譯器使用逃逸分析決定哪些變量應該在棧上分配,哪些變量應該在堆上分配。d6f28資訊網(wǎng)——每日最新資訊28at.com

其中包括使用 new、make 和字面量等方法隱式分配的內(nèi)存,Go 語言的逃逸分析遵循以下兩個不變性:d6f28資訊網(wǎng)——每日最新資訊28at.com

  1. 指向棧對象的指針不能存在于堆中;
  2. 指向棧對象的指針不能在棧對象回收后存活。

什么意思呢?我們來翻譯一下:d6f28資訊網(wǎng)——每日最新資訊28at.com

  • 首先,如果堆的指針指向了棧對象,那么棧對象的內(nèi)存就需要分配到堆上;
  • 如果棧對象回收后,指針還存活,那么這個對象就只能分配到堆上。

我們在進行內(nèi)存分配時,編譯器會遵循上述兩個原則,對我們申請的變量或?qū)ο筮M行內(nèi)存分配到棧上或者是堆上。d6f28資訊網(wǎng)——每日最新資訊28at.com

換言之,當我們分配內(nèi)存時,違反了上述兩個原則之一,本來想分配到棧上的變量可能就會“逃逸”到堆上,被稱作內(nèi)存逃逸。如果程序中出現(xiàn)大量的內(nèi)存逃逸,勢必會帶來意外的負面影響:比如垃圾回收緩慢,內(nèi)存溢出等問題。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

4.2 四種逃逸場景

Go 語言中,由于以下四種情況,棧上的內(nèi)存可能會發(fā)生逃逸。d6f28資訊網(wǎng)——每日最新資訊28at.com

1. 指針逃逸

指針逃逸很容易理解,我們在函數(shù)中創(chuàng)建一個對象時,對象的生命周期隨著函數(shù)結(jié)束而結(jié)束,這時候?qū)ο蟮膬?nèi)存就分配在棧上。d6f28資訊網(wǎng)——每日最新資訊28at.com

而如果返回了一個對象的指針,這種情況下,函數(shù)雖然退出了,但指針還在,對象的內(nèi)存不能隨著函數(shù)結(jié)束而回收,因此只能分配在堆上。d6f28資訊網(wǎng)——每日最新資訊28at.com

package maintype User struct {ID     int64Name   stringAvatar string}// 要想不發(fā)生逃逸,返回 User 對象即可。func GetUserInfo() *User {return &User{ID: 666666,Name: "sim lou",Avatar: "https://www.baidu.com/avatar/666666",}}func main() {u := GetUserInfo()println(u.Name)}

上面例子中,如果返回的是 User 對象,而非對象指針 *User,那么它就是一個局部變量,會分配在棧上;反之,指針作為引用,在 main 函數(shù)中還會繼續(xù)使用,因此內(nèi)存只能分配到堆上。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

我們可以用編譯器命令 go build -gcflags -m main.go 來查看變量逃逸的情況:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

&User{...} escapes to heap 即表示對象逃逸到堆上了。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

2. interface{} 動態(tài)類型逃逸

在 Go 語言中,空接口即 interface{} 可以表示任意的類型,如果函數(shù)參數(shù)為 interface{},編譯期間很難確定其參數(shù)的具體類型,也會發(fā)生逃逸。比如 Println 函數(shù),入?yún)⑹且粋€ interface{} 空類型:d6f28資訊網(wǎng)——每日最新資訊28at.com

func Println(a ...interface{}) (n int, err error)

d6f28資訊網(wǎng)——每日最新資訊28at.com

這時,返回的是一個 User 對象,也會發(fā)生對象逃逸,但逃逸節(jié)點是 fmt.Println 函數(shù)使用時:d6f28資訊網(wǎng)——每日最新資訊28at.com

func GetUserInfo() User {return User{ID: 666666,Name: "sim lou",Avatar: "https://www.baidu.com/avatar/666666",}}func main() {u := GetUserInfo()fmt.Println(u.Name) // 對象發(fā)生逃逸}

d6f28資訊網(wǎng)——每日最新資訊28at.com

3. 棧空間不足

操作系統(tǒng)對內(nèi)核線程使用的棧空間是有大小限制的,64 位 Linux 系統(tǒng)上通常是 8 MB。可以使用 ulimit -a 命令查看機器上棧允許占用的內(nèi)存的大小。d6f28資訊網(wǎng)——每日最新資訊28at.com

root@cvm_172_16_10_34:~ # ulimit -a-s: stack size (kbytes)             8192-u: processes                       655360-n: file descriptors                655360

因為棧空間通常比較小,因此遞歸函數(shù)實現(xiàn)不當時,容易導致棧溢出。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

對于 Go 語言來說,運行時(runtime) 嘗試在 goroutine 需要的時候動態(tài)地分配棧空間,goroutine 的初始棧大小為 2 KB。當 goroutine 被調(diào)度時,會綁定內(nèi)核線程執(zhí)行,棧空間大小也不會超過操作系統(tǒng)的限制。d6f28資訊網(wǎng)——每日最新資訊28at.com

對 Go 編譯器而言,超過一定大小的局部變量將逃逸到堆上,不同的 Go 版本的大小限制可能不一樣。我們來做一個實驗(注意,分配 int[] 時,int 占 8 字節(jié),所以 8192 個 int 就是 64 KB):d6f28資訊網(wǎng)——每日最新資訊28at.com

package mainimport "math/rand"func generate8191() {nums := make([]int, 8192) // <= 64KBfor i := 0; i < 8192; i++ {nums[i] = rand.Int()}}func generate8192() {nums := make([]int, 8193) // > 64KBfor i := 0; i < 8193; i++ {nums[i] = rand.Int()}}func generate(n int) {nums := make([]int, n) // 不確定大小for i := 0; i < n; i++ {nums[i] = rand.Int()}}func main() {generate8191()generate8192()generate(1)}

d6f28資訊網(wǎng)——每日最新資訊28at.com

編譯結(jié)果如下:d6f28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片d6f28資訊網(wǎng)——每日最新資訊28at.com

可以發(fā)現(xiàn),make([]int, 8192) 沒有發(fā)生逃逸,make([]int, 8193) 和 make([]int, n) 逃逸到堆上。也就是說,當切片占用內(nèi)存超過一定大小,或無法確定當前切片長度時,對象占用內(nèi)存將在堆上分配。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

4. 閉包

一個函數(shù)和對其周圍狀態(tài)(lexical environment,詞法環(huán)境)的引用捆綁在一起(或者說函數(shù)被引用包圍),這樣的組合就是閉包(closure)。也就是說,閉包讓你可以在一個內(nèi)層函數(shù)中訪問到其外層函數(shù)的作用域。d6f28資訊網(wǎng)——每日最新資訊28at.com

— 閉包d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

Go 語言中,當使用閉包函數(shù)時,也會發(fā)生內(nèi)存逃逸。看一則示例代碼:d6f28資訊網(wǎng)——每日最新資訊28at.com

package mainfunc Increase() func() int {   n := 0   return func() int {       n++       return n  }}func main() {   in := Increase()   fmt.Println(in()) // 1   fmt.Println(in()) // 2}

Increase() 函數(shù)的返回值是一個閉包函數(shù),該閉包函數(shù)訪問了外部變量 n,那變量 n 將會一直存在,直到 in 被銷毀。很顯然,變量 n 占用的內(nèi)存不能隨著函數(shù) Increase() 的退出而回收,因此將會逃逸到堆上。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

4.3 利用逃逸分析提升性能

傳值VS傳指針d6f28資訊網(wǎng)——每日最新資訊28at.com

傳值會拷貝整個對象,而傳指針只會拷貝指針地址,指向的對象是同一個。傳指針可以減少值的拷貝,但是會導致內(nèi)存分配逃逸到堆中,增加垃圾回收(GC)的負擔。在對象頻繁創(chuàng)建和刪除的場景下,傳遞指針導致的 GC 開銷可能會嚴重影響性能。d6f28資訊網(wǎng)——每日最新資訊28at.com

一般情況下,對于需要修改原對象值,或占用內(nèi)存比較大的結(jié)構(gòu)體,選擇傳指針。對于只讀的占用內(nèi)存較小的結(jié)構(gòu)體,直接傳值能夠獲得更好的性能。d6f28資訊網(wǎng)——每日最新資訊28at.com

d6f28資訊網(wǎng)——每日最新資訊28at.com

5. 小結(jié)

內(nèi)存分配是程序運行時內(nèi)存管理的核心邏輯,Go 程序運行時的內(nèi)存分配器使用類似 TCMalloc 的分配策略將對象根據(jù)大小分類,并設計多層緩存的組件提高內(nèi)存分配器的性能。d6f28資訊網(wǎng)——每日最新資訊28at.com

理解 Go 語言內(nèi)存分配器的設計與實現(xiàn)原理,可以幫助我們理解不同編程語言在設計內(nèi)存分配器時做出的不同選擇。d6f28資訊網(wǎng)——每日最新資訊28at.com

棧內(nèi)存是應用程序中重要的內(nèi)存空間,它能夠支持本地的局部變量和函數(shù)調(diào)用,棧空間中的變量會與棧一同創(chuàng)建和銷毀,這部分內(nèi)存空間不需要工程師過多的干預和管理,現(xiàn)代的編程語言通過逃逸分析減少了我們的工作量,理解棧空間的分配對于理解 Go 語言的運行時有很大的幫助。d6f28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-51839-0.html深入淺出內(nèi)存管理:空間分配及逃逸分析

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 如何給開源項目發(fā)起提案

下一篇: 揭秘 Go 中 Goroutines 輕量級并發(fā)

標簽:
  • 熱門焦點
  • 一加Ace2 Pro官宣:普及16G內(nèi)存 引領24G

    一加官方今天繼續(xù)為本月發(fā)布的新機一加Ace2 Pro帶來預熱,公布了內(nèi)存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引領,還有呢?#一加Ace2Pro#,2023 年 8 月,敬請期待。”同時
  • 5月iOS設備性能榜:M1 M2依舊是榜單前五

    和上個月一樣,沒有新品發(fā)布的iOS設備性能榜的上榜設備并沒有什么更替,僅僅只有跑分變化而產(chǎn)生的排名變動,剛剛開始的蘋果WWDC2023,推出的產(chǎn)品也依舊是新款Mac Pro、新款Mac Stu
  • 服務存儲設計模式:Cache-Aside模式

    Cache-Aside模式一種常用的緩存方式,通常是把數(shù)據(jù)從主存儲加載到KV緩存中,加速后續(xù)的訪問。在存在重復度的場景,Cache-Aside可以提升服務性能,降低底層存儲的壓力,缺點是緩存和底
  • SpringBoot中使用Cache提升接口性能詳解

    環(huán)境:springboot2.3.12.RELEASE + JSR107 + Ehcache + JPASpring 框架從 3.1 開始,對 Spring 應用程序提供了透明式添加緩存的支持。和事務支持一樣,抽象緩存允許一致地使用各
  • 每天一道面試題-CPU偽共享

    前言:了不起:又到了每天一到面試題的時候了!學弟,最近學習的怎么樣啊 了不起學弟:最近學習的還不錯,每天都在學習,每天都在進步! 了不起:那你最近學習的什么呢? 了不起學弟:最近在學習C
  • 一條抖音4億人圍觀 ! 這家MCN比無憂傳媒還野

    作者:Hiu 來源:互聯(lián)網(wǎng)品牌官01 擦邊少女空降熱搜,幕后推手曝光被網(wǎng)友譽為&ldquo;純欲天花板&rdquo;的女網(wǎng)紅井川里予,近期因為一組哥特風照片登上熱搜,引發(fā)了一場互聯(lián)網(wǎng)世界關于
  • 小米公益基金會捐贈2500萬元馳援北京、河北暴雨救災

    8月2日消息,今日小米科技創(chuàng)始人雷軍在其微博上發(fā)布消息稱,小米公益基金會宣布捐贈2500萬元馳援北京、河北暴雨救災。攜手抗災,京冀安康!以下為公告原文
  • 質(zhì)感不錯!OPPO K11渲染圖曝光:旗艦IMX890傳感器首次下放

    一直以來,OPPO K系列機型都保持著較為均衡的產(chǎn)品體驗,歷來都是2K價位的明星機型,去年推出的OPPO K10和OPPO K10 Pro兩款機型憑借各自的出色配置,堪稱有
  • 滴滴違法違規(guī)被罰80.26億 共存在16項違法事實

    滴滴違法違規(guī)被罰80.26億 存在16項違法事實開始于2121年7月,歷經(jīng)一年時間,網(wǎng)絡安全審查辦公室對“滴滴出行”網(wǎng)絡安全審查終于有了一個暫時的結(jié)束。據(jù)“網(wǎng)信
Top 主站蜘蛛池模板: 阿坝| 上杭县| 垦利县| 甘泉县| 平遥县| 永州市| 龙泉市| 惠水县| 大英县| 临泉县| 巴青县| 同德县| 河西区| 田阳县| 昭苏县| 大石桥市| 会泽县| 武义县| 台北县| 墨竹工卡县| 石门县| 大化| 海南省| 盐山县| 罗山县| 常山县| 桃园县| 会泽县| 吉林省| 乐至县| 临沂市| 东港市| 宁明县| 高尔夫| 永和县| 东乌| 沙坪坝区| 元氏县| 荔波县| 安仁县| 沁源县|