【轉】JVM運行原理及JVM中的Stack和Heap的實現過程

來自: http://blog.csdn.net//u011067360/article/details/46047521java

 

Java語言寫的源程序經過Java編譯器,編譯成與平臺無關的‘字節碼程序’(.class文件,也就是0,1二進制程序),而後在OS之上的Java解釋器中解釋執行,而JVM是java的核心和基礎,在java編譯器和os平臺之間的虛擬處理器。緩存

1、JVM原理數據結構

一、JVM簡介:模塊化

JVM是java的核心和基礎,在java編譯器和os平臺之間的虛擬處理器。它是一種利用軟件方法實現的抽象的計算機基於下層的操做系統和硬件平臺,能夠在上面執行java的字節碼程序。spa

java編譯器只要面向JVM,生成JVM能理解的代碼或字節碼文件。Java源文件經編譯成字節碼程序,經過JVM將每一條指令翻譯成不一樣平臺機器碼,經過特定平臺運行。操作系統


2.Java語言運行的過程.net

Java語言寫的源程序經過Java編譯器,編譯成與平臺無關的‘字節碼程序’(.class文件,也就是0,1二進制程序),而後在OS之上的Java解釋器中解釋執行。線程



簡單點的話也和下面的原理差很少
翻譯


三、JVM執行程序的過程 :

I.加載。class文件

II.管理並分配內存

III.執行垃圾收集

JRE(java運行時環境)由JVM構造的java程序的運行環境
設計




2、JVM中的Stack和Heap

在JVM中,內存分爲兩個部分,Stack(棧)和Heap(堆),這裏,咱們從JVM的內存管理原理的角度來認識Stack和Heap,並經過這些原理認清Java中靜態方法和靜態屬性的問題。

一、簡介

Stack(棧)是JVM的內存指令區。Stack管理很簡單,push必定長度字節的數據或者指令,Stack指針壓棧相應的字節位移;pop必定字節長度數據或者指令,Stack指針彈棧。Stack的速度很

快,管理很簡單,而且每次操做的數據或者指令字節長度是已知的。因此Java 基本數據類型,Java 指令代碼,常量都保存在Stack中。

Heap(堆)是JVM的內存數據區。Heap 的管理很複雜,每次分配不定長的內存空間,專門用來保存對象的實例。在Heap 中分配必定的內存來保存對象實例,實際上也只是保存對象實例的

屬性值,屬性的類型和對象自己的類型標記等,並不保存對象的方法(方法是指令,保存在Stack中),在Heap 中分配必定的內存保存對象實例和對象的序列化比較相似。而對象實例在Heap 

中分配好之後,須要在Stack中保存一個4字節的Heap 內存地址,用來定位該對象實例在Heap 中的位置,便於找到該對象實例。


下圖爲JVM的體系結構


二、什麼是數據、什麼是指令,對象的方法和對象的屬性又是什麼?

1)方法自己是指令的操做碼部分,保存在Stack中;

2)方法內部變量做爲指令的操做數部分,跟在指令的操做碼以後,保存在Stack中(其實是簡單類型保存在Stack中,對象類型在Stack中保存地址,在Heap 中保存值);上述的指令操做碼和指令操做數構成了完整的Java 指令。


3)對象實例包括其屬性值做爲數據,保存在數據區Heap 中。

非靜態的對象屬性做爲對象實例的一部分保存在Heap 中,而對象實例必須經過Stack中保存的地址指針才能訪問到。所以可否訪問到對象實例以及它的非靜態屬性值徹底取決於可否得到對象實例在Stack中的地址指針。


三、非靜態方法和靜態方法的區別:

非靜態方法有一個和靜態方法很重大的不一樣:非靜態方法有一個隱含的傳入參數,該參數是JVM給它的,和咱們怎麼寫代碼無關,這個隱含的參數就是對象實例在Stack中的地址指針。所以靜態方法(在Stack中的指令代碼)老是能夠找到本身的專用數據(在Heap 中的對象屬性值)。固然非靜態方法也必須得到該隱含參數,所以非靜態方法在調用前,必須先new一個對象實例,得到Stack中的地址指針,不然JVM將沒法將隱含參數傳給非靜態方法。


靜態方法無此隱含參數,所以也不須要new對象,只要class文件被ClassLoader load進入JVM的Stack,該靜態方法便可被調用。固然此時靜態方法是存取不到Heap 中的對象屬性的。

小結::當一個class文件被ClassLoader load進入JVM後,方法指令保存在Stack中,此時Heap 區沒有數據。而後程序技術器開始執行指令,若是是靜態方法,直接依次執行指令代碼,固然此時指令代碼是不能訪問Heap 數據區的;若是是非靜態方法,因爲隱含參數沒有值,會報錯。所以在非靜態方法執行前,要先new對象,在Heap 中分配數據,並把Stack中的地址指針交給非靜態方法,這樣程序技術器依次執行指令,而指令代碼此時可以訪問到Heap 數據區了。


靜態屬性和動態屬性:

前面提到對象實例以及動態屬性都是保存在Heap 中的,而Heap 必須經過Stack中的地址指針纔可以被指令(類的方法)訪問到。

所以能夠推斷出:靜態屬性是保存在Stack中的,而不一樣於動態屬性保存在Heap 中。正由於都是在Stack中,而Stack中指令和數據都是定長的,所以很容易算出偏移量,也所以無論什麼指令(類的方法),均可以訪問到類的靜態屬性。也正由於靜態屬性被保存在Stack中,因此具備了全局屬性。


在JVM中,靜態屬性保存在Stack指令內存區,動態屬性保存在Heap數據內存區。


總結:

1)、棧是運行時的單位,而堆是存儲的單位。

2)、棧解決程序的運行問題,即程序如何執行,或者說如何處理數據;堆解決的是數據存儲的問題,即數據怎麼放、放在哪兒。


四、爲何要把堆和棧區分出來呢?

第一,從軟件設計的角度看,棧表明了處理邏輯,而堆表明了數據。這樣分開,使得處理邏輯更爲清晰。分而治之的思想。這種隔離、模塊化的思想在軟件設計的方方面面都有體現。

第二,堆與棧的分離,使得堆中的內容能夠被多個棧共享(也能夠理解爲多個線程訪問同一個對象)。這種共享的收益是不少的。一方面這種共享提供了一種有效的數據交互方式(如:共享內存),另外一方面,堆中的共享常量和緩存能夠被全部棧訪問,節省了空間。

第三,棧由於運行時的須要,好比保存系統運行的上下文,須要進行地址段的劃分。因爲棧只能向上增加,所以就會限制住棧存儲內容的能力。而堆不一樣,堆中的對象是能夠根據須要動態增加的,所以棧和堆的拆分,使得動態增加成爲可能,相應棧中只需記錄堆中的一個地址便可。

第四,面向對象就是堆和棧的完美結合。其實,面向對象方式的程序與之前結構化的程序在執行上沒有任何區別。可是,面向對象的引入,使得對待問題的思考方式 發生了改變,而更接近於天然方式的思考。當咱們把對象拆開,你會發現,對象的屬性其實就是數據,存放在堆中;而對象的行爲(方法),就是運行邏輯,放在棧 中。咱們在編寫對象的時候,其實即編寫了數據結構,也編寫的處理數據的邏輯。


程序要運行老是有一個起點的。同C語言同樣,java中的Main就是那個起點。不管什麼java程序,找到main就找到了程序執行的入口:)


五、堆中存什麼?棧中存什麼?

1)、堆中存的是對象。棧中存的是基本數據類型和堆中對象的引用。一個對象的大小是不可估計的,或者說是能夠動態變化的,可是在棧中,一個對象只對應了一個4btye的引用。

2)、爲何不把基本類型放堆中呢?因 爲其佔用的空間通常是1~8個字節——須要空間比較少,並且由於是基本類型,因此不會出現動態增加的狀況——長度固定,所以棧中存儲就夠了,若是把他存在 堆中是沒有什麼意義的(還會浪費空間,後面說明)。能夠這麼說,基本類型和對象的引用都是存放在棧中,並且都是幾個字節的一個數,所以在程序運行時,他們 的處理方式是統一的。可是基本類型、對象引用和對象自己就有所區別了,由於一個是棧中的數據一個是堆中的數據。最多見的一個問題就是,Java中參數傳遞 時的問題。

3)、Java中的參數傳遞時傳值呢?仍是傳引用?程序運行永遠都是在棧中進行的,於是參數傳遞時,只存在傳遞基本類型和對象引用的問題。不會直接傳對象自己。

Java在方法調用傳遞參數時,由於沒有指針,因此它都是進行傳值調用


PS:堆和棧中,棧是程序運行最根本的東西。程序運行能夠沒有堆,可是不能沒有棧。而堆是爲棧進行數據存儲服務,說白了堆就是一塊共享的內存。不過,正是由於堆和棧的分離的思想,才使得Java的垃圾回收成爲可能。

相關文章
相關標籤/搜索