偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Stack vs Heap:棧區(qū)分配內存快還是堆區(qū)分配內存快 ?

存儲 存儲架構
棧區(qū)是自動管理的,堆區(qū)是手動管理的,顯然在棧區(qū)上分配內存要比在堆區(qū)上更快,當在棧區(qū)上申請的內存使用場景有限,程序員申請內存時還要更多的依靠堆區(qū),但是在棧區(qū)申請的內存滿足要求的情況我個人更傾向于使用棧區(qū)內存。

大家好,我是小風哥。

后臺有讀者問到底是從棧上分配內存快還是從堆上分配內存快,這是個比較基礎的問題,今天就來聊一聊。

棧區(qū)的內存申請與釋放

毫無疑問,顯然從棧上分配內存更快,因為從棧上分配內存僅僅就是棧指針的移動而已,這是什么意思呢?什么叫做“棧指針的移動”?以x86平臺為例,在棧上分配內存是怎樣實現(xiàn)的呢?很簡單,就一行指令:

sub $0x40,%rsp

這行代碼就叫做“棧指針的移動”,其本質就是這張圖:

很簡單,寄存器esp中保存的是當前棧的棧頂?shù)刂?,由于棧的增長方向是從高地址到低地址,因此增大棧時需要將棧指針向下移動,即sub指令的作用,這條指令將棧頂指針向下移動了64字節(jié)(0x40),因此可以說在棧上分配了64字節(jié)。

可以看到,在棧上分配內存其實非常非常簡單,簡單到就只有一條機器指令。

而棧區(qū)的內存釋放也非常簡單,也是只需要一條機器指令:

leave

leave指令的作用是將?;焚x值給esp,這樣棧指針指向上一個棧幀的棧頂,然后pop出ebp,這樣ebp就指向上一個棧幀的棧底:

看到了吧,執(zhí)行完leave指令后ebp以及esp就指向上一個棧幀了,這就相當于棧幀的彈出,pop,這樣stack 1占用的內存就無效了,沒有任何用處了,顯然這就是我們常說的內存回收,因此簡單的一條leave指令就可以回收掉棧區(qū)中的內存。

關于棧、棧幀與棧區(qū),更詳細的講解可以參考我寫的這篇《??函數(shù)運行時在內存中是什么樣子???》。

接下來我們看到堆區(qū)的內存申請與釋放。

堆區(qū)的內存申請與釋放

與棧區(qū)分配內存相對的是堆內存分配,堆區(qū)分配內存有多復雜呢?

在堆區(qū)上申請與釋放內存是一個相對復雜的過程,因為堆本身是需要程序員(內存分配器實現(xiàn)者)自己管理的,而棧是編譯器來維護的,堆區(qū)的維護同樣涉及內存的分配與釋放,但這里的內存分配與釋放顯然不會像棧區(qū)那樣簡單,一句話,這里是按需進行內存的分配與釋放,本質在于堆區(qū)中每一塊被分配出去的內存其生命周期都不一樣,這是由程序員決定的,我傾向于把內存動態(tài)分配釋放想象成去停車場找停車位。

這顯然會讓問題復雜起來,我們必須小心的維護哪些內存是已經(jīng)分配出去的以及哪些是空閑的、該怎樣找到一塊空閑的內存、該怎樣回收程序員不需要的內存塊、同時還不能有嚴重的內存碎片問題,棧區(qū)分配釋放內存都無需關心這些問題,于此同時當堆區(qū)內存空間不足時還需要擴大堆區(qū)等等,這些都使得在堆區(qū)申請內存要比在棧區(qū)分配內存復雜的多。

說了這么多,那么在堆區(qū)上申請內存要比在棧上申請內存慢多少呢?

接下來我們寫段代碼實驗一下。

show me the code

void test_on_stack() {
int a = 10;
}

void test_on_heap() {
int* a = (int*)malloc(sizeof(int));
*a = 10;
free(a);
}

void test() {
auto begin = GetTimeStampInUs();
for (int i = 0; i < 100000000; ++i) {
test_on_stack();
}
cout<<"test on stack "<<((GetTimeStampInUs() - begin) / 1000000.0)<<endl;

begin = GetTimeStampInUs();
for (int i = 0; i < 100000000; ++i) {
test_on_heap();
}
cout<<"test on heap "<<((GetTimeStampInUs() - begin) / 1000000.0)<<endl;
}

這段代碼非常簡單,這里有兩個函數(shù):

  • test_on_stack函數(shù)中定義一個局部變量,這就是從棧上申請一個整數(shù)大小的內存空間
  • test_on_heap函數(shù)從堆上申請一個整數(shù)大小的內存空間

然后我們在測試函數(shù)中分別調用這兩個函數(shù),每一個調用1億次,記錄下需要運行的時間,得到的測試結果為:

test on stack 0.191008
test on heap 20.0215

可以看到,在棧上總耗時只有大概0.2s,而在堆上分配的耗時為20s,相差百倍。

值得注意的是,這里在編譯程序時沒有開啟編譯優(yōu)化,開啟編譯優(yōu)化后的耗時是這樣的:

test on stack 0.033521
test on heap 0.039294

可以看到,相差無幾,可這是為什么呢?顯然從常理推斷在棧上分配要更快一些,問題會出在哪里呢?

既然我們開啟了編譯優(yōu)化,那是不是優(yōu)化后的代碼運行的更快了呢,我們來看下編譯優(yōu)化后生成的指令都有啥:

test_on_stackv:
400f85: 55 push %rbp
400f86: 48 89 e5 mov %rsp,%rbp
400f89: 5d pop %rbp
400f8a: c3 retq

test_on_heapv:
400f8b: 55 push %rbp
400f8c: 48 89 e5 mov %rsp,%rbp
400f8f: 5d pop %rbp
400f90: c3 retq

啊哈,編譯器實在是太聰明了,它顯然注意到這兩個函數(shù)中的代碼實際上啥也沒干,即使我們還專門為變量a賦值為了10,但后續(xù)我們根本就沒有用到變量a,因此編譯器給我們生成了一個空函數(shù),上面這些機器指令實際上對應一個空函數(shù)。

小風哥反復在這里添加代碼都沒有騙過編譯器,我試圖加大變量a賦值的復雜度,編譯器依然很聰明的生成了一個空函數(shù),反正我是沒有試出來,可見現(xiàn)代編譯器是足夠智能的,生成的機器指令效率很高,關于該怎樣寫出一個更好的benchmark,從而讓我們可以看到在開啟編譯優(yōu)化的情況下這兩種內存分配方式的對比,歡迎任何對此有心得或者對編譯優(yōu)化有心得的同學留言。

最后讓我們來看看這兩種內存分配方式的定位。

棧內存與堆內存的差異

首先我們必須意識到,棧是一種先進后出的結構,棧區(qū)會隨著函數(shù)調用層級的增加而增大,而隨著函數(shù)調用完成而減少,因此棧是無需任何“管理”的;與此同時由于棧的這種性質,在棧上申請的內存其生命周期是和函數(shù)綁定在一起,當函數(shù)調用完成后其占用的棧幀內存將無效,且棧的大小是有限的,你不能在棧上申請過多內存,就像這樣一段C代碼:

void test() {
int b[10000000];
b[1000000] = 10;
}

這段代碼運行起來后會core掉,原因就在于棧區(qū)大小是非常有限的,在棧上分配一大塊數(shù)據(jù)會讓棧撐爆掉,這就是所謂的Stack Overflow:

額。。。不好意思,圖放錯了,應該是這個Stack Overflow:

不好意思,又放錯了,總之你懂得。

而堆則不同,在堆上分配的內存其生命周期是受程序員控制的,程序員決定什么時候申請內存,什么時候釋放內存,因此堆是必須被管理起來的,堆區(qū)是一片很廣闊的區(qū)域,堆區(qū)空間不足時會向操作系統(tǒng)請求擴大堆區(qū)從而獲得更多地址空間。

當然,堆區(qū)在給程序員更大靈活性的同時需要程序員確保內存在不被使用時釋放掉,否則會內存泄漏,在棧上申請內存則不存這個問題。

總結

棧區(qū)是自動管理的,堆區(qū)是手動管理的,顯然在棧區(qū)上分配內存要比在堆區(qū)上更快,當在棧區(qū)上申請的內存使用場景有限,程序員申請內存時還要更多的依靠堆區(qū),但是在棧區(qū)申請的內存滿足要求的情況我個人更傾向于使用棧區(qū)內存。

本文轉載自微信公眾號「碼農的荒島求生」,可以通過以下二維碼關注。轉載本文請聯(lián)系碼農的荒島求生公眾號。

責任編輯:武曉燕 來源: 碼農的荒島求生
相關推薦

2009-06-03 15:52:34

堆內存棧內存Java內存分配

2013-05-17 15:38:22

iOS開發(fā)iOS堆棧heap stack

2021-03-22 11:51:22

Java內存棧上

2022-05-05 10:00:53

Kafka分區(qū)分配Linux

2021-07-14 10:00:32

Python內存測量

2020-05-27 21:13:27

JavaJVM內存

2021-02-28 13:22:54

Java內存代碼

2017-03-01 10:45:39

Linux驅動技術內存申請

2010-09-25 14:12:50

Java內存分配

2010-09-17 16:14:22

Java內存分配

2010-09-25 15:40:52

配置JVM內存

2021-04-23 07:27:31

內存分配CPU

2013-10-12 13:01:51

Linux運維內存管理

2021-12-16 06:52:33

C語言內存分配

2011-07-15 01:10:13

C++內存分配

2022-01-13 10:30:21

C語言內存動態(tài)

2018-02-08 14:57:22

對象內存分配

2022-03-07 10:54:34

內存Linux

2023-10-18 13:31:00

Linux內存

2025-07-01 02:25:00

點贊
收藏

51CTO技術棧公眾號