相信大多數Javaer對Java的內存結構都有必定的瞭解,但若是對於Java的內存結構只停留的"堆","棧"中顯然是不夠的。今天來給你們詳細談一談Java的內存區域結構,本文基於 JDK7 的內存結構作講解,JDK8的內存結構加上了metaspace,有些許變更,想詳細瞭解的同窗請自行翻閱相關資料。算法
文章結構數組
- 內存結構圖
- 根據內存結構圖各個區域作詳細講解
圖片說明緩存
- 方法區,堆區(標綠)爲全部線程共享的內存區域,虛擬機棧,本地方法棧,程序計數器(標藍)爲線程似有的內存區域,即線程隔離的。
代碼的運行是有順序的,但當CPU在多線程間切換時,當從A線程切換到B線程,再切回到A線程時,CPU如何知道該從A線程的哪裏繼續執行呢?CPU工做時就是根據每一個線程的程序計數器的值來選取下一條須要執行的字節碼指令,即"找到它離開時的位置來繼續執行"。須要提示的是,當CPU執行的是一個Java方法時,程序計數器記錄的是正在執行的虛擬機字節碼指令的地址。若是執行的是Native方法,這個計數器值爲Undefined,即不發揮做用。多線程
虛擬機棧描述的是Java方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀用於存儲局部變量表,操做數棧,動態連接,方法出口等信息。每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧和出棧的過程。
局部變量表存放了編譯期間能夠知道大小的各類類型變量,它所須要的內存空間大小在編譯期間就已經分配,當一個方法被調用時,棧幀進入虛擬機棧,在運行期間,局部變量表大小是不會變化的。併發
本地方法棧與虛擬機棧鎖發揮的做用是很是類似的,它們之間的區別不過是虛擬機棧爲虛擬機執行Java方法服務,而本地方法棧爲虛擬機執行Native方法服務。須要注意的是,因爲虛擬機規範對於本地方法棧的具體實現沒有作強制要求,因此Sun HotSpot直接把本地方法棧和虛擬機棧合二爲一。函數
Java堆是Javaer須要重點關注的一塊區域,由於涉及到內存的分配(new關鍵字,反射等)與回收(回收算法,收集器等),這裏不作太多詳細的介紹內存的分配與回收,後期有時間專門出博客講解。在Java虛擬機規範中的描述是:全部對象實例以及數組都要在堆上分配。須要特別注意的是,線程共享的Java堆中可能分出多個線程私有的分配緩衝區(TLAB,這是爲了併發分配內存時的髒分配問題,須要使用相關參數來開啓。虛擬機默認使用CAS加上失敗重試機制解決髒分配問題)。此外,Java堆在HotSpot中的實現是可擴展的。參數-Xmx/-Xms來控制。post
方法區用於存儲已經被虛擬機加載的類信息,常量("zdy","123"等),靜態變量(static變量)等數據。方法區有一個別名叫永久代(Permanent Generation),這是由於HotSpot設計團隊把GC分代收集擴展至方法區,這樣HotSpot的垃圾收集器能夠像管理Java堆同樣管理這部份內存,可以省錢專門爲方法區編寫內存管理代碼的工做。對於其餘虛擬機(J9)等,是沒有永久代這個概念的。
永久代的配置參數: -XX:MaxPermSize性能
運行時常量池是方法區的一部分,用於存放編譯期生成的各類字面量("zdy","123"等)和符號引用。運行時常量池具備動態性,並不是只有Class文件中的內容才能進入運行時常量池,運行期間也能將新的常量放入池中。如String.intern()方法。spa
直接內存不是Java虛擬機規範的內存區域。可是這部分也被Javaer頻繁使用,並且也會致使OutOfMember異常。因此這裏順帶提一提。
在JDK4中加入的NIO,使用了Native函數庫直接分配堆外內存,而後經過一個緩存在Java堆中的Buffer來指向這塊地址進行操做。這樣可以避免Java堆和Native堆來回複製數據,在某些場景能夠顯著提升性能。
直接內存不受任何虛擬機參數控制,但很明顯,你不能大於物理內存大小。線程
結語
JDK7的內存模型就大體介紹完了,JVM系列博客後期將帶給你們更加深刻的內容,下期預告:JVM系列之實戰內存溢出異常