內存是操做系統中不可或缺的部分,一臺機器有內存才能正常穩定地運行。編程語言從來都有本身的內存管理機制,尤爲是Java,它不像C同樣須要本身維護內存的關係,而是經過本身的內部機制JVM來管理內存。這雖然下降了開發同窗們的入手難度,但同時也使得在運行時一旦拋出內存異常,很難知道發生了什麼。如下就簡單地來介紹一下JVM內存的結構。編程
廢話很少說,先看下面圖表:數據結構
▲圖表1 JVM總體結構▲多線程
此時可能有些人會一頭霧水,彆着急,咱們一點點來看。編程語言
先看第一部分:學習
▲圖表2 類裝載系統▲優化
這個表明了一個類被裝入JVM的過程。若是用更簡單的比喻,它相似初學JDBC時Class.forName()所作的事情。具體來說,會分爲如下幾步:spa
- 加載:將字節碼文件按照雙親委託機制(當某個類加載器須要加載某個.class文件時,它首先把這個任務委託給他的上級類加載器,遞歸這個操做。若是上級的類加載器沒有加載,本身才會去加載這個類,方塊中對應的即是Bootstrap Class Loader(啓動類加載器),Extension Class Loader (標準擴展類加載器),Application Class Loader(系統類加載器))進行加載;
- 連接字節碼文件:分爲三個步驟,分別是字節碼驗證(verify)、class類數據結構分析(prepare)以及相應的內存分配和最後的符號表的連接(resolve);
- 初始化操做:好比類中靜態屬性和初始化賦值,以及靜態塊的執行等。
一個類就是這樣被裝入了內存,那麼在程序中會發生什麼呢?彆着急,且聽我慢慢道來:操作系統
▲圖表3 Java內存結構▲線程
這張圖是運行時數據區,表示了當前JVM的全部狀態,讓咱們一個區一個區看看:3d
1. 方法區(Method Area):用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼。運行時常量池也是方法區的一部分,好比String w = 」hello」;中,hello就被放在了方法區裏。方法區是線程共享的。有一點要注意,JDK1.8 使用元空間 MetaSpace 替代方法區,元空間並不在 JVM中,而是使用本地內存;
_2. 堆區(Heap Area):_堆區是JVM中佔地最大的區域,全部的實例對象所有都在堆區上,這個位置也是線程共享的;
3. 棧區(Stack Area):存放了每個線程的當前狀態,每個線程都有一個本身的棧,而棧中存放了如下數據組成的一個個棧幀:操做數、局部變量表、動態連接、返回地址,須要注意的是,棧中只存引用或者基本類型,並且線程不共享(並無指內部的優化動做);
4. 程序計數器(PC Registers): 它是當前線程執行字節碼的行號指示器。在多線程中,爲了讓每一個線程切換回來後可以恢復原來執行的指令,就須要爲每一個線程啓動一個PC計數器,這些計數器之間是互補影響的,由於程序計數器和棧同樣都是線程私有的。固然程序計數器是JVM惟一個不會出現內存溢出的組件;
5. 本地方法棧(Native Method Statck):保存了本地方法,它是當程序調用類庫(本地方法)中的方法時纔會用到它,即native method。
接下來就要介紹爲咱們勤勤懇懇工做的執行引擎啦:
▲圖表4 Java執行引擎▲
Java執行分爲編譯執行(JIT compilation)和解釋執行(Interpreter)。
首先咱們要明白什麼是編譯執行,什麼是解釋執行:
Bash就是屬於解釋執行語言,一行行解釋代碼來完成命令;C就是編譯執行, 將文件編譯成字節碼,接着運行。
那爲何Java會用兩套編譯手段呢?先看下面這個例子:
假定你是導演,寫了個劇本,讓演員表演。
一種方式是讓演員把整個劇本都背下來,吃透到腦子裏,而後連續表演一個小時。另外一種方式是讓演員表演兩分鐘,再看兩分鐘腳本,思考一下,再表演兩分鐘,再看一會腳本,思考一下…
單純從效率上來說,第一種方式必定會比第二種方式表演起來更熟練,可是現實每每不容許,或者沒必要要。若是不是很是須要表演的技巧,簡單地看一下劇本就好啦。在特別考驗表演技巧時,才須要背下整個劇本,這樣才能在表演時更好地展示本身的風采。
Java也是如此,由JIT發現熱代碼後,將指令集優化(好比重排,合併),而後生成字節碼供系統運行。至於其餘的代碼呢,簡單的解釋執行完就行了~
在運行的過程當中必定會產生不少的垃圾,由於隨着系統運行,不少對象都會廢棄不用,此時就須要使用垃圾回收機制Garbage Collection(垃圾回收內容太多了,之後能夠單拿出來說講)。
最後,來看下Java與系統底層交互:
▲圖表5 Java與系統底層交互▲
JNI(Java本地接口)經過使用Java本地接口書寫程序,能夠確保代碼在不一樣的平臺上方便移植。經過JNI實現與本地方法庫的調用交互,使得在Java虛擬機內運行的Java代碼可以與其它編程語言互相操做,包括_建立本地方法、更新Java對象、調用Java方法,_引用Java類,捕捉和拋出異常等,也容許Java代碼調用 C/C++或彙編語言編寫的庫。
好啦,今天就到這裏,相信你們對JVM的內存模型已經有了必定的認知,但願在遇到這種問題時,本篇內容能夠幫到正在閱讀的你。學習技術是一個日積月累的過程,切不可急躁~若是寫代碼有天意,那必定是讓你修煉!