Java虛擬機在執行Java程序的過程當中都會把所管理的內存劃分爲若干個不一樣的區域,下面就是幾個運行時的數據區域:
java
咱們逐個來看看各自的領域:多線程
1.程序計數器spa
程序計數器就是一塊較小的內存空間,它的做用能夠看做是當前線程所執行的字節碼的行號指示器,說白就就是經過這個計數器的值來選取下一條須要執行的字節碼指令,就是記錄程序行走的步驟,什麼循環、跳轉、異常處理都是依賴這個計數器完成的。就拿個例子來講說,咱們的Java的多線程是經過線程以前的輪流切換來實現的,一個線程中止後,另一個線程啓動,那麼回來的時候怎麼知道本身執行到哪裏呢,就用這個程序計數器來記錄了。命令行
2.Java虛擬機棧線程
虛擬機棧描述的是Java方法執行的內存模型:每一個方法被執行的時候都會同時建立一個棧幀用於存儲局部變量表、操做數棧、動態連接、方法出口等信息,每個方法被調用直至執行完成的過程,就對應着一個棧幀入棧到出棧的過程。指針
其實不少人把Java內存區分爲堆和棧,這種分法不許確,內部的分法遠比這複雜,這裏須要指出的是,咱們說的Java棧其實就是這裏的虛擬機棧了。code
3.本地方法棧對象
本地方法棧與虛擬機棧的做用很是的類似,區別是虛擬機棧爲虛擬機執行Java方法服務,本地方法棧爲虛擬機使用到的Native方法服務。blog
4.Java堆接口
堆是Java虛擬機所管理的內存中最大的一塊,Java堆是被全部線程共享的一塊內存區域,在虛擬機啓動時建立,這個內存區域的惟一目的就是存放對象實例,幾乎全部對象的實例都是在這裏分配內存的。
堆其實也是垃圾收集器管理的主要區域,不少時候也被稱爲「GC堆」。
5.方法區:
方法區跟堆是同樣的,是全部線程共享的內存區域,它用於存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼。
6.運行時常量池
這個運行時常量池也是方法區的一部分,Class文件中除了有類的版本、ziduan 、方法、接口等描述信息外,還有一項信息是常量池,用於放編譯期生成的各類字面量和符號引用,這部份內容將在類加載後存放在方法去的運行時常量池中。
咱們來看看對象訪問是如何進行的?
很簡單的一句:Object object = new Object();執行了這句話後內存這樣執行的:「Object object」這部分產生的結果就是在Java棧的本地變量表上,有一個reference類型數據的出現,而「new Object()」這部分的結果就是在堆上造成一塊存儲了Object類型的內存,另外,在堆上還有能查到此對象類型數據(父類、實現的接口、方法)的地址信息,這個東西真正存儲在方法區中。
不一樣的虛擬機實現對象的訪問有所不一樣,下面介紹這兩種。
1.句柄訪問方式
Java堆中將會劃分出一塊內存來作句柄池,存儲的就是對象的句柄地址,包含對象實例數據和類型數據的具體地址信息。
2.直接指針訪問方式
就在堆中生成一塊內存區,裏面既有類型數據的指針也有對象實例數據。
咱們來看看JVM中溢出的各類狀況吧,瞭解瞭解:
1.Java堆溢出
java堆是用於存儲對象的,若是咱們不斷的建立對象的話,在對象數大於對的最大容量的時候,就會發生溢出。
public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while(true) { list.add(new OOMObject()); } } }
這樣的話咱們就能夠在命令行下看到這樣的狀況:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
這個就是堆溢出的標誌了。
2.虛擬機棧和本地方法棧溢出
棧說白就是跟方法的調用有關,若是咱們不斷的調用方法,局部變量多,棧的容量就很快會滿。
public class StackTest { public void method() { method(); } public static void main(String[] args) { StackTest stack = new StackTest(); try { stack.method(); } catch (Exception e) { e.printStackTrace(); } } }
你運行下看看就會出現下面的錯誤了:
Exception in thread "main" java.lang.StackOverflowError
哈哈,這個就是棧的溢出,請記住它。
3.運行時常量池溢出
要想看到這個溢出就要不停的往運行時常量池添加東西,這裏咱們要介紹一個方法:String.intern(),這個是Native方法,也就是本地的方法,用法:若是池中包含一個等於此String對象的字符串,則返回此String對象,不然就將這個字符串添加到運行時常量池中,返回此String對象,下面咱們就用這個方法來讓運行時常量池溢出。
public class RuntimeConstantPoolTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(); int i = 0; while(true) { list.add(String.valueOf(i++).intern()); } } }
而後就會出現下面的錯誤:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
PermGen space的全稱是Permanent Generation space,是指內存的永久保存區域,這塊內存主要是被JVM存放Class和Meta信息的,也就是說這個運行時常量池是放在方法區的,看看方法區的定義就知道了。