java虛擬機所管理的內存包含如下幾個運行時數據區域:java
1.方法區(method Area) 2.虛擬機棧(vm stack) 3.本地方法棧(native method stack) 4.堆(heap) 5.程序計數器算法
1和4是由全部線程共享的數據區 2,3,5是線程隔離的數據區數組
程序計數器:多線程
這是一塊很小的內存空間,能夠看做是當前線程所執行的字節碼的行號指示器。jvm
在jvm的概念模型裏,字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令。函數
另外 分支 循環 跳轉 異常處理 線程恢復等基礎功能都須要他來完成。線程
JVM多線程實現:經過線程輪流切換並分配處理器執行時間的方式來實現的。設計
因此爲了切換換成後能恢復到正確的執行位置,每條線程都要有一個獨立計數器,互不影響,獨立存儲,咱們稱這類內存區域爲線程私有的內存。對象
若是線程正在執行java方法,則計數器記錄的是正在執行的虛擬機字節碼指定地址。若是正在執行native方法,則計數器值爲空。接口
程序計數器是jvm中沒有規定內存溢出錯誤的區域。
棧:
棧是線程私有的,生命週期和線程相同。虛擬機棧描述的是java方法執行的內存模型:每一個方法在執行的時候都會建立一個棧幀,用於存儲局部變量表,操做數棧,動態連接,方法出口等信息。每個方法從調用直到執行完成的過程,對應着一個棧幀在jvm中入棧到出棧的過程。
棧被稱爲局部變量表。存放各類基本數據類型,對象引用和returnAddress類型(指向一條字節碼指令的地址)
64位的long和double會佔用2個局部變量空間slot,其他數據類型佔1個。棧須要的內存空間,編譯時期就完成分配。
jvm規範中定義的兩種異常:若是線程請求的棧深度大於虛擬機鎖容許的深度將拋出stackOverflowError異常。若是棧拓展時沒法申請到足夠內存,就會拋出內存溢出異常。
本地方法:
他和棧發揮的做用差很少,區別是棧爲虛擬機執行java方法服務,本地方法棧爲虛擬機使用到的native服務。本地方法棧和棧拋出的異常同樣。
堆:
java堆是jvm虛擬機管理的內存中的最大一塊。是被全部線程共享的一塊內存區域。在虛擬機啓動時建立,目的是存放對象實例。幾乎全部的對象都在這裏分配內存。jvm虛擬機規範中說明:全部的對象實例以及數組都要在堆上建立。
java堆是垃圾收集器的主要區域。也被成爲GC堆。收集器如今都採用分代收集算法。因此java堆細分一下爲:
新生代和老年代。更細緻的有Eden空間,From Survivor空間,To Survivor空間。
線程共享的java堆中可能劃分出多個線程私有的分配緩衝區。
java堆能夠處於物理上不連續的內存空間中,邏輯上是連續的便可。他的大小是有-Xmx 和 -Xms 控制。
若是在堆中沒有內存完成實例分配,而且堆也沒法拓展時,會拋出內存溢出。
經典問題:堆和棧,其實java中不止這兩個內存區域。
方法區:
方法區和堆差很少,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。它也叫 non-heap 非堆。
HotSpot中設計團隊將GC分代收集拓展到了方法區,這樣容易遇到內存溢出狀況,永久代有-XX:MaxPermSize上限。JDK7貌似正在使用native Memory管理方法區。
運行時常量池:
它是方法區的一部分,class中除了類的版本 字段 方法 接口等外還有就是常量池。用於存放編譯期生成的各類字面量和符號引用。這部分常量池內容將在類加載後進入方法區的運行時常量池中存放。
運行期間也能夠將新的常量放入池中。
直接內存:
直接內存不屬於jvm運行時內存數據區。
JDK1.4z中新加入的NIO 引入了一種基於通道與緩衝區的IO方式,它可使用native函數庫直接分配堆外內存。而後經過一個存儲在java堆中的DirectByteBuffer對象做爲這塊內存的引用進行操做。
HotSpot虛擬機對象探祕:
虛擬機中對象的建立:
虛擬機遇到一條new指令時,首先將檢查這個指令的參數是否能在常量池中定位到一個類的符號引用。而且檢查這個符號引用表明的類是否已經被加載,解析和初始化過。若是沒有,要先執行相應的類加載過程。類加載檢查經過後,jvm將爲新生對象分配內存,對象所需內存大小在類加載完成後即可徹底肯定。