本節(jié)是操作系統(tǒng)系列教程的第三篇文章,屬于操作系統(tǒng)第一章即基礎(chǔ)篇,在真正開始操作系統(tǒng)相關(guān)章節(jié)前在這一部分回顧一些重要的主題,以下是目錄,由于本文篇幅較多因此會(huì)按上篇、中篇、下篇三次發(fā)布,目錄中黑體為本篇內(nèi)容。
什么是內(nèi)存
C/C++內(nèi)存模型
**堆區(qū)與棧區(qū)的本質(zhì)
**
**
**Java內(nèi)存模型
**
**Jave中的堆區(qū)與棧區(qū)是如何實(shí)現(xiàn)的
**
Python內(nèi)存模型
指針與引用
進(jìn)程的內(nèi)存模型
幻想大師-操作系統(tǒng)
總結(jié)
堆與棧的本質(zhì)是什么
在編程語(yǔ)言中,堆區(qū)和棧區(qū)本質(zhì)上都是內(nèi)存,因此二者在本質(zhì)上沒有任何區(qū)別,只不過這兩塊內(nèi)存的使用方式是不一樣的。
在數(shù)據(jù)結(jié)構(gòu)與算法中,我們也有堆和棧的概念,但那里指的不是內(nèi)存,而是兩種數(shù)據(jù)結(jié)構(gòu)。
你可能會(huì)想,我們?yōu)槭裁匆M(fèi)盡心力的提出堆和棧這兩個(gè)概念呢?之所以需要區(qū)分兩種內(nèi)存用法,根源在于: 內(nèi)存是有限的 。
如果計(jì)算機(jī)內(nèi)存是無限的,那么我們根本就不用這么麻煩的給內(nèi)存劃分兩個(gè)區(qū)域,在其中的一個(gè)區(qū)域中這樣使用內(nèi)存,另一區(qū)域那樣使用內(nèi)存,這些都是不需要的。即使在今天PC內(nèi)存普遍都在8G、16G,這依然是不夠的,因此我們需要合理的來安排內(nèi)存的使用,堆和棧就是為達(dá)到這一目的而采用的技術(shù)。
你會(huì)發(fā)現(xiàn)棧其實(shí)是一種非常巧妙的內(nèi)存使用方法。函數(shù)調(diào)用完成后,函數(shù)運(yùn)行過程中占用的內(nèi)存就會(huì)被釋放掉,這樣,只要程序員代碼寫的合理(棧幀不至于過大),那我們程序就可以一直運(yùn)行下去,而不會(huì)出現(xiàn)內(nèi)存不足的現(xiàn)象。程序員在棧區(qū)不需要擔(dān)心內(nèi)存分配釋放問題,因?yàn)檫@一切都是自動(dòng)進(jìn)行的。而如果程序員想自己控制內(nèi)存,那么可以選擇在堆上進(jìn)行內(nèi)存分配。因此這里提供了兩種選擇,一種是“自動(dòng)的”,一種是“手動(dòng)的”,目的都是在合理使用內(nèi)存的同時(shí)提供給程序員最大的靈活性。
堆和棧是計(jì)算機(jī)科學(xué)中很優(yōu)秀的設(shè)計(jì)思想,這種設(shè)計(jì)思想充分的體現(xiàn)了計(jì)算機(jī)如何合理且靈活的使用有限資源。
堆區(qū)和棧區(qū)對(duì)C/C++程序員來說就是實(shí)實(shí)在在的內(nèi)存,而對(duì)于Java、Python等語(yǔ)言的程序員來說又該如何理解內(nèi)存呢?
Java、Python等內(nèi)存模型
當(dāng)Java、Python等語(yǔ)言的程序在執(zhí)行時(shí)其解釋器的內(nèi)存布局同樣如下圖所示,我們之前講過,解釋器也是一個(gè)C/C++程序,因此這里的代碼段包含的是解釋器的實(shí)現(xiàn)代碼而不是Java、Python等代碼,這一點(diǎn)大家一定要注意。
"C/C++程序員面對(duì)的是實(shí)實(shí)在在的物理內(nèi)存,Java、Python等程序面對(duì)的是解釋器。"
C/C++分配內(nèi)存是直接在物理內(nèi)存中進(jìn)行的,而Java、Python等程序是將內(nèi)存分配請(qǐng)求交給解釋器,解釋器再去物理內(nèi)存上進(jìn)行分配。希望大家務(wù)必理解這一點(diǎn)。
Java、Python等程序員是看不到如下圖所示的內(nèi)存布局的,因?yàn)檫@一切都是解釋器才能看到的,解釋器對(duì)Java、Python等程序員屏蔽了這些。Java、Python等程序員也無需關(guān)心解釋器的內(nèi)存布局。
Java、Python等程序的一大優(yōu)點(diǎn)就是內(nèi)存的自動(dòng)化管理,而C/C++程序員需要自己來管理從堆上分配的內(nèi)存。內(nèi)存管理這一項(xiàng)工作在Java、Python等程序中被解釋器接管了,解釋器的這項(xiàng)功能被稱為“垃圾回收器”。
在非C/C++語(yǔ)言中,我們來看兩個(gè)有代表性的語(yǔ)言,首先我們看一下Java。
Java內(nèi)存模型
Java的內(nèi)存模型中同樣有棧和堆這樣的概念,如下圖所示,在Java函數(shù)中我們定義的內(nèi)置數(shù)據(jù)類型比如int a = 0,是直接存放在棧上的,引用類型,也就是用new關(guān)鍵字定義的變量是分配在堆上的。和C/C++一樣,每個(gè)Java函數(shù)在執(zhí)行時(shí)都有自己的棧幀。隨著函數(shù)的調(diào)用,棧不斷的擴(kuò)大。當(dāng)函數(shù)調(diào)用完畢后棧幀被回收,在堆上分配的變量依然可以被后續(xù)函數(shù)使用。Java程序員無需像C/C++程序員一樣需要關(guān)心內(nèi)存回收的問題,這一切都是Java的解釋器JVM來管理的。
在用法上Java中的堆和棧和C/C++是一樣的,只不過Java程序員無需關(guān)心內(nèi)存的釋放問題。但是好奇的同學(xué)可能會(huì)問,C/C++中的堆和棧我已經(jīng)清楚了,因?yàn)镃/C++程序運(yùn)行時(shí)在內(nèi)存中的樣子已經(jīng)在《C/C++內(nèi)存模型》這一小節(jié)中詳細(xì)的講述了,那么Java中的堆和棧在內(nèi)存中是什么樣子的呢,就是和上圖一樣嗎?要回答這個(gè)問題,就要涉及到Java中的堆和棧是如何實(shí)現(xiàn)的。
Java中的堆和棧是如何實(shí)現(xiàn)的
如果你自己設(shè)計(jì)過一門語(yǔ)言的話,你應(yīng)該會(huì)很清楚這個(gè)問題。
我們先回答上一節(jié)中提到的問題,那就是Java中的堆和棧就是如上圖所示的那樣嗎?是這樣的,作為Java程序員在寫代碼時(shí)腦海里有上面這張圖基本上就夠用了。但是,Java中的堆和棧不同于C/C++當(dāng)中的堆和棧。
我們已經(jīng)知道Java中的內(nèi)存管理其實(shí)是解釋器JVM來搞定的,作為C/C++程序,JVM的內(nèi)存布局就如下圖所示。
一般情況下,當(dāng)JVM運(yùn)行一個(gè)Java函數(shù)時(shí)需要在堆上創(chuàng)建出Java函數(shù)的棧幀,然后把這些棧幀放入棧中(這里的棧指的是具有先進(jìn)后出性質(zhì)的數(shù)據(jù)結(jié)構(gòu))。希望大家不要被這句話繞暈,這里出現(xiàn)了兩個(gè)“?!?,但是含義完全不同。
-
Java棧幀:指的是上圖中我們看到的棧。
-
棧幀放入到棧:我們?cè)跀?shù)據(jù)結(jié)構(gòu)課程中都學(xué)過棧,棧有push和pop兩種操作,把棧幀放入棧指的是把棧幀push到JVM所持有的棧這種數(shù)據(jù)結(jié)構(gòu)當(dāng)中,以此來模擬C/C++程序執(zhí)行過程中函數(shù)棧幀先進(jìn)后出的這種性質(zhì),當(dāng)一個(gè)Java函數(shù)被執(zhí)行完畢后,JVM pop掉該函數(shù)的棧幀。
如果你想在代碼級(jí)別來理解這個(gè)過程,大體上可以參考下面的代碼,注意JVM是C/C++程序,這里的代碼是一個(gè)極其簡(jiǎn)單的描述。你可以看到如何組織棧幀完全是JVM設(shè)計(jì)者來決定的,只要棧幀具備先進(jìn)后出的性質(zhì)就可以。
void RunJavaFunction(JVM* jvm, string javaFunction) {
// 在堆上申請(qǐng)一塊空間,用于存放java棧幀
stackFrame* frame = (stackFrame*) malloc(sizeof(stackFrame));
// 把要使用的棧幀push到JVM的函數(shù)調(diào)用棧中
jvm->stack->push(frame);
// 在申請(qǐng)的棧幀上執(zhí)行Java函數(shù)
run(javaFunction, frame);
// 執(zhí)行完畢后pop掉該函數(shù)棧幀
jvm->stack->pop();
}
JVM會(huì)在自己的堆中為用new修飾的對(duì)象創(chuàng)建內(nèi)存,這里的堆就是如上圖所示的堆,是可以要記住JVM是一個(gè)C/C++程序,JVM看到的堆才是如上圖所示的那樣。所以你會(huì)發(fā)現(xiàn),一般情況下, Java中的棧和Java對(duì)象都是JVM在自己的堆上分配出來的 ,這就是Java中堆和棧是如何實(shí)現(xiàn)的。
在講解完Java的內(nèi)存模型后,我們來看一下Python的。
Python內(nèi)存模型
Python的內(nèi)存模型和Java其實(shí)是類似的,Java程序員腦海中的那張圖同樣適用于Python程序員。
Python語(yǔ)言中的解釋器比較多,比如CPython,PyPy等,在這里我們以Python默認(rèn)的解釋器CPython為例來說明,我們已經(jīng)知道了解釋器其實(shí)也是一個(gè)C程序,CPython也不例外,下圖左側(cè)就是我們已經(jīng)熟悉的C/C++內(nèi)存布局,我們把堆區(qū)放大,如下圖右側(cè)所示。我們可以看到Python的解釋器把自己的堆區(qū)劃分成了兩部分,分別是Object-specific memory區(qū)域,以及Python core區(qū)域:
Object-specific memory這個(gè)區(qū)域?qū)iT用來存放PyObject。你也許已經(jīng)知道了,Python中所有的數(shù)據(jù)類型比如int,dict,str等都是一個(gè)對(duì)象,叫做PyObject。當(dāng)我們?cè)赑ython中創(chuàng)建一個(gè)變量比如dict時(shí),CPython就會(huì)在堆區(qū)的上半部分(Object-specific memory)中分配一塊內(nèi)存,創(chuàng)建一個(gè)PyObject,這個(gè)PyObject用來存放我們的dict。
Python core:所有非PyObject的內(nèi)存請(qǐng)求都在這里分配的。
所以你會(huì)發(fā)現(xiàn),Python中所有的內(nèi)存同樣是解釋器在自己的堆上分配的。
-
JAVA
+關(guān)注
關(guān)注
19文章
2972瀏覽量
104865 -
堆棧
+關(guān)注
關(guān)注
0文章
182瀏覽量
19791 -
python
+關(guān)注
關(guān)注
56文章
4800瀏覽量
84820
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論