內(nèi)存管理

內(nèi)容包括:

  • C++內(nèi)存管理
  • Java內(nèi)存管理

C++內(nèi)存管理

內(nèi)存分配方式

在C++中,內(nèi)存分成5個(gè)區(qū),分別是棧、堆、自由存儲區(qū)、全局(靜態(tài)、全局)、常量區(qū)。

?? : 存放函數(shù)參數(shù)以及局部變量,在出作用域時(shí),將自動被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但分配的內(nèi)存容量有限。
?? : new分配的內(nèi)存塊(包括數(shù)組,類實(shí)例等),需delete手動釋放。如果未釋放,在整個(gè)程序結(jié)束后,OS會幫你回收掉。
自由存儲區(qū) : malloc分配的內(nèi)存塊,需free手動釋放,它和堆有些相似。
全局/靜態(tài)區(qū) : 全局變量(global)和靜態(tài)變量(static)存于此處。(在以前的C語言中,全局變量又分為初始化的和未初始化的,C++不分)
常量存儲區(qū) : 常量(const)存于此處,此存儲區(qū)不可修改。

棧和堆的區(qū)別

主要區(qū)分有以下幾點(diǎn)
(1).管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。

(2).空間大小:一般來講在32位系統(tǒng)下,堆內(nèi)存可以達(dá)到4G的空間,從這個(gè)角度來看堆內(nèi)存幾乎是沒有什么限制的。但是對于棧來講,一般都是有一定的空間大小的,例如,在VC6下面,默認(rèn)的??臻g大小是1M(好像是,記不清楚了)。

(3).碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個(gè)問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對應(yīng),以至于永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內(nèi)容已經(jīng)被彈出,詳細(xì)的可以參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。

(4).生長方向:對于堆來講,生長方向是向上的,也就是向著內(nèi)存地址增加的方向;對于棧來講,它的生長方向是向下的,是向著內(nèi)存地址減小的方向增長。

(5).分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)。

(6).分配效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會分到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。

Java內(nèi)存管理

內(nèi)存分配方式

Java虛擬機(jī)在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干不同的數(shù)據(jù)區(qū)域,這些區(qū)域都有各自的用途以及創(chuàng)建和銷毀的時(shí)間。Java虛擬機(jī)所管理的內(nèi)存將會包括以下幾個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)域,如下圖所示:

Java運(yùn)行時(shí)數(shù)據(jù)區(qū)

程序序計(jì)數(shù)器
????程序計(jì)數(shù)器,可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。在虛擬機(jī)的概念模型里,字節(jié)碼解釋器工作就是通過改變程序計(jì)數(shù)器的值來選擇下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都要依賴這個(gè)計(jì)數(shù)器來完成。
????多線程中,為了讓線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線程之間互不影響、獨(dú)立存儲,因此這塊內(nèi)存是 線程私有 的。
????當(dāng)線程正在執(zhí)行的是一個(gè)Java方法,這個(gè)計(jì)數(shù)器記錄的是在正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;當(dāng)執(zhí)行的是Native方法,這個(gè)計(jì)數(shù)器值為空。此內(nèi)存區(qū)域是唯一一個(gè)沒有規(guī)定任何OutOfMemoryError情況的區(qū)域 。

Java虛擬機(jī)棧
????Java虛擬機(jī)棧也是線程私有的 ,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會創(chuàng)建一個(gè)棧幀用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈表、方法出口信息等。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程。
????局部變量表中存放了編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用和returnAddress類型(指向了一條字節(jié)碼指令的地址)。如果擴(kuò)展時(shí)無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryError異常。

本地方法棧
????本地方法棧與虛擬機(jī)的作用相似,不同之處在于虛擬機(jī)棧為虛擬機(jī)執(zhí)行的Java方法服務(wù),而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)。有的虛擬機(jī)直接把本地方法棧和虛擬機(jī)棧合二為一。會拋出stackOverflowError和OutOfMemoryError異常。

Java堆
????Java堆是所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動時(shí)創(chuàng)建,此內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例 。
????Java堆是垃圾收集器管理的主要區(qū)域。由于現(xiàn)在收集器基本采用分代回收算法,所以Java堆還可細(xì)分為:新生代和老年代。從內(nèi)存分配的角度來看,線程共享的Java堆中可能劃分出多個(gè)線程私有的分配緩沖區(qū)(TLAB)。
????Java堆可以處于物理上不連續(xù)的內(nèi)存空間,只要邏輯上連續(xù)的即可。在實(shí)現(xiàn)上,既可以實(shí)現(xiàn)固定大小的,也可以是擴(kuò)展的。如果堆中沒有內(nèi)存完成實(shí)例分配,并且堆也無法完成擴(kuò)展時(shí),將會拋出OutOfMemoryError異常。

方法區(qū)
????方法區(qū)是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù) 。
????相對而言,垃圾收集行為在這個(gè)區(qū)域比較少出現(xiàn),但并非數(shù)據(jù)進(jìn)了方法區(qū)就永久的存在了,這個(gè)區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和對類型的卸載。當(dāng)方法區(qū)無法滿足內(nèi)存分配需要時(shí),將拋出OutOfMemoryError異常。
????運(yùn)行時(shí)常量池是方法區(qū)的一部分,它用于存放編譯期生成的各種字面量和符號引用。

直接內(nèi)存
????直接內(nèi)存不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,在NIO類中引入一種基于通道與緩沖區(qū)的IO方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個(gè)存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作。
????直接內(nèi)存的分配不會受到Java堆大小的限制,但是會受到本機(jī)內(nèi)存大小的限制,所有也可能會拋OutOfMemoryError異常。

參考文獻(xiàn)
(1).C++-內(nèi)存管理(整理筆記)
(2).Java內(nèi)存管理原理及內(nèi)存區(qū)域詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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