日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

堆和栈的区别(面试经验总结)

發布時間:2023/11/27 生活经验 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 堆和栈的区别(面试经验总结) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C++中,內存分為5個區:堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區。

棧:是由編譯器在需要時自動分配,不需要時自動清除的變量存儲區。通常存放局部變量、函數參數等。
堆:是由new分配的內存塊,由程序員釋放(編譯器不管),一般一個new與一個delete對應,一個new[]與一個delete[]對應。如果程序員沒有釋放掉, ? ? ? ?資源將由操作系統在程序結束后自動回收。

自由存儲區:是由malloc等分配的內存塊,和堆十分相似,用free來釋放。
全局/靜態存儲區:全局變量和靜態變量被分配到同一塊內存中(在C語言中,全局變量又分為初始化的和未初始化的,C++中沒有這一區分)。
常量存儲區:這是一塊特殊存儲區,里邊存放常量,不允許修改。
(注意:堆和自由存儲區其實不過是同一塊區域,new底層實現代碼中調用了malloc,new可以看成是malloc智能化的高級版本)

一. 堆與棧的討論:
?管理方式:堆中資源由程序員控制(容易產生memory?leak),?棧資源由編譯器自動管理,無需手工控制。
?系統響應:對于堆,應知道系統有一個記錄空閑內存地址的鏈表當系統收到程序申請時,遍歷該鏈表,尋找第一個空間大于申請空間的堆結點,刪 除空閑結點鏈表中的該結點,并將該結點空間分配給程序(大多數系統會在這塊內存空間首地址記錄本次分配的大小,這樣delete才能正確釋放本內存 ?空間,另外系統會將多余的部分重新放入空閑鏈表中)。對于棧,只要棧的剩余空間大于所申請空間,系統為程序提供內存,否則報異常提示棧出。
?空間大小:?堆是不連續的內存區域(因為系統是用鏈表來存儲空閑內存地址,自然不是連續的),堆大小受限于計算機系統中有效的虛擬內存(32bit ?系統理論上是4G),所以堆的空間比較靈活,比較大。棧是一塊連續的內存區域,大小是操作系統預定好的,windows下棧大小是2M(也有是1M,在 ?編譯時確定,VC中可設置)。
?碎片問題:對于堆,頻繁的new/delete會造成大量碎片,使程序效率降低。?對于棧,它是一個先進后出的隊列,進出一一對應,不會產生碎片。
?生長方向堆向上,向高地址方向增長。棧向下,向低地址方向增長。
?分配方式:?堆都是動態分配(沒有靜態分配的堆)。棧有靜態分配和動態分配,靜態分配由編譯器完成(如局部變量分配),動態分配由alloca函數分配,但棧的動態分配的資源由編譯器進行釋放,無需程序員實現。

?分配效率:堆由C/C++函數庫提供,機制很復雜。所以堆的效率比棧低很多。棧是極其系統提供的數據結構計算機在底層對棧提供支持,分配專門 ?寄存 ?器存放棧地址,棧操作有專門指令

int b;
int a=0; //全局初始化區  
char *p1; //全局未初始化區  
mian(){
int b;//棧  
char s[]="abc";//棧  
char *p2;//棧  
char *p3="123456"; // 123456/0在常量區,p3在棧上。  
static int c=0; // 全局(靜態)初始化區 
p1=(char *)malloc(10); // 分配得來得10和20字節的區域就在堆區。  
p2=(char *)malloc(20);
strcpy(p1,"123456");//123456/0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方
}

堆和棧的區別可以用如下的比喻來看出:

? ?使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。(經典!)

下面就小姐一下java中的堆和棧和c++中略有不同描述偶這使用的酵素來說,先了解JVM(虛擬機)內存的劃分:

????? Java程序在運行時都要開辟空間,任何軟件在運行時都要在內存中開辟空間,Java虛擬機運行時也是要開辟空間的。JVM運行時在內存中開辟一片內存區域,啟動時在自己的內存區域中進行更細致的劃分,因為虛擬機中每一片內存處理的方式都不同,所以要單獨進行管理。

????? JVM內存的劃分有五片: 1.???寄存器;2.???本地方法區;? 3.???方法區;? 4.???棧內存;? 5.???堆內存。

堆(Heap)

? ? ?Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建;

? ? ?Java虛擬機規范描述:所有的對象實例及數組都要在堆上分配;

? ? ?Java堆可以處于物理上不連續的內存空間,只要邏輯上連續即可;

? ? ?(線程共享)堆內存中的對象對所有線程可見。堆內存中的對象可以被所有線程訪問;

? ? ?(異常提示)如果是堆內存沒有可用的空間存儲生成的對象,JVM會拋出java.lang.OutOfMemoryError;

? ? ?(內存分配)動態分配內存

棧(Stack)

? ? 存放基本類型的數據和對象的引用,即存放變量;

? ? ? ?如果存放的是基本類型數據(非靜態變量),則直接將變量名和值存入stack中的內存中;

? ? 如果是引用類型,則將變量名存入棧,然后指向它new出的對象(存放在堆中);

? ? ?(線程私有)棧內存歸屬于單個線程,每個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存可以理解成線程的私有內存;

? ? ?(內存分配)棧的內存要遠遠小于堆內存,如果你使用遞歸的話,那么你的棧很快就會充滿。如果遞歸沒有及時跳出,很可能發生StackOverFlowError問題;

? ? ?(異常提示)如果棧內存沒有可用的空間存儲方法調用和局部變量,JVM會拋出java.lang.StackOverFlowError;

? ? ?(內存分配)內存分配固定;

? ? ?存取速度比堆要快,僅次于寄存器,棧數據可以共享;

?????? 我們重點來說一下堆和棧:

?????? 棧內存:棧內存首先是一片內存區域,存儲的都是局部變量,凡是定義在方法中的都是局部變量(方法外的是全局變量),for循環內部定義的也是局部變量,是先加載函數才能進行局部變量的定義,所以方法先進棧,然后再定義變量,變量有自己的作用域,一旦離開作用域,變量就會被釋放。棧內存的更新速度很快,因為局部變量的生命周期都很短。

? ? ?? 堆內存:存儲的是數組和對象(其實數組就是對象)凡是new建立的都是在堆中,堆中存放的都是實體(對象),實體用于封裝數據,而且是封裝多個(實體的多個屬性),如果一個數據消失,這個實體也沒有消失,還可以用,所以堆是不會隨時釋放的,但是棧不一樣,棧里存放的都是單個變量,變量被釋放了,那就沒有了。堆里的實體雖然不會被釋放,但是會被當成垃圾,Java有垃圾回收機制不定時的收取。

????? 下面我們通過一個圖例詳細講一下堆和棧:

????? 比如主函數里的語句 ? int [] arr=new int [3];在內存中是怎么被定義的:

????? 主函數先進棧,在棧中定義一個變量arr,接下來為arr賦值,但是右邊不是一個具體值,是一個實體。實體創建在堆里,在堆里首先通過new關鍵字開辟一個空間,內存在存儲數據的時候都是通過地址來體現的,地址是一塊連續的二進制,然后給這個實體分配一個內存地址。數組都是有一個索引,數組這個實體在堆內存中產生之后每一個空間都會進行默認的初始化(這是堆內存的特點,未初始化的數據是不能用的,但在堆里是可以用的,因為初始化過了,但是在棧里沒有),不同的類型初始化的值不一樣。所以堆和棧里就創建了變量和實體:

?????????????????????????????????????????????????

???? 那么堆和棧是怎么聯系起來的呢?

???? 我們剛剛說過給堆分配了一個地址,把堆的地址賦給arr,arr就通過地址指向了數組。所以arr想操縱數組時,就通過地址,而不是直接把實體都賦給它。這種我們不再叫他基本數據類型,而叫引用數據類型。稱為arr引用了堆內存當中的實體。(可以理解為c或c++的指針,Java成長自c++和c++很像,優化了c++)?????? ? ??????????????????????????????????????????????????????

?????

????????????? 如果當int [] arr=null;

????????????? arr不做任何指向,null的作用就是取消引用數據類型的指向。

????????????? 當一個實體,沒有引用數據類型指向的時候,它在堆內存中不會被釋放,而被當做一個垃圾,在不定時的時間內自動回收,因為Java有一個自動回收機制,(而c++沒有,需要程序員手動回收,如果不回收就越堆越多,直到撐滿內存溢出,所以Java在內存管理上優于c++)。自動回收機制(程序)自動監測堆里是否有垃圾,如果有,就會自動的做垃圾回收的動作,但是什么時候收不一定。

???????????? 所以堆與棧的區別很明顯:

??????????? 1.棧內存存儲的是局部變量而堆內存存儲的是實體;

??????????? 2.棧內存的更新速度要快于堆內存,因為局部變量的生命周期很短;

??????????? 3.棧內存存放的變量生命周期一旦結束就會被釋放,而堆內存存放的實體會被垃圾回收機制不定時的回收。

附 棧數據共享

? ?棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:

? ? ?int a = 3;

? ? ?int b = 3;

? ?編譯器先處理int a = 3;首先它會在棧中創建一個變量為a的引用,然后查找棧中是否有3這個值,如果沒找到,就將3存放進來,然后將a指向3。接著處理int b = 3;在創建完b的引用變量后,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。

? ?這時,如果再令a=4;那么編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進來,并令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。

? ?要注意這種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改并不會影響到b, 它是由編譯器完成的,它有利于節省空間。而一個對象引用變量修改了這個對象的內部狀態,會影響到另一個對象引用變量。

? ?String是一個特殊的包裝類數據。可以用:

? ?String str = new String("abc");

? ?String str = "abc";

? 兩種的形式來創建,第一種是用new()來新建對象的,它會在存放于堆中。每調用一次就會創建一個新的對象。

而第二種是先在棧中創建一個對String類的對象引用變量str,然后查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,并令str指向”abc”,如果已經有”abc”則直接令str指向“abc”。

? ?比較類里面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。

? ?String str1 = "abc";

? ?String str2 = "abc";

? ?System.out.println(str1==str2); //true

? ?可以看出str1和str2是指向同一個對象的。

? ?String str1 =new String ("abc");

? ?String str2 =new String ("abc");

? ?System.out.println(str1==str2); // false

? ?用new的方式是生成不同的對象。每一次生成一個。

? ?因此用第一種方式創建多個”abc”字符串,在內存中其實只存在一個對象而已. 這種寫法有利于節省內存空間. 同時它可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對于String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字符串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。

? ?另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能并沒有被創建!而可能只是指向一個先前已經創建的 對象。只有通過new()方法才能保證每次都創建一個新的對象。

? ?由于String類的immutable性質,當String變量需要經常變換其值時,應該考慮使用StringBuffer(線程安全,效率低)類,以提高程序效率。

引用:

劍指Offer——簡述堆和棧的區別_IT全棧 華強工作室-CSDN博客_堆和棧的區別

堆和棧的概念和區別_pt666的博客-CSDN博客_堆和棧的區別

C++堆和棧的區別和聯系_yangyongUestc的博客-CSDN博客_c++堆和棧

總結

以上是生活随笔為你收集整理的堆和栈的区别(面试经验总结)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。