理解JVM運行時的數據區是Java編程中的進階部分。咱們在開發中都遇到過一個很頭疼的問題就是OutOfMemoryError(內存溢出錯誤),可是若是咱們瞭解JVM的內部實現和其運行時的數據區的工做機制,那麼前面的問題就會迎刃而解。在這片文章中,咱們將簡單瞭解JVM中有哪些運行時數據區以及這些數據區的工做機制。html
程序計數器 (Program Counter (PC) Register)java
JVM棧 (Java Virtual Machine Stacks)ios
堆內存 (Heap Memory)編程
方法區 (Method Area)數組
運行時常量池 (Run-time Constant Pool)數據結構
本地方法棧 (Native Method Stacks)多線程
查看上面的圖,能夠得知以上六個數據區能夠分爲線程私有仍是共享,整體分爲以下兩種。jvm
屬於這一種的數據區包含 程序計數器, JVM棧還有本地方法棧。 每一個線程都私有這三個數據區,這些數據區在其所屬的線程建立時初始化,並隨着所屬線程結束被銷燬。編程語言
在通用的計算機體系中,程序計數器用來記錄當前正在執行的指令,在JVM中也是如此。程序計數器是線程私有,因此當一個新的線程建立時,程序計數器也會建立。因爲Java是支持多線程,Java中的程序計數器用來記錄當前線程中正在執行的指令。若是當前正在執行的方法是本地方法,那麼此刻程序計數器的值爲undefined。注意這個區域是惟一一個不拋出OutOfMemoryError的運行時數據區。post
在介紹JVM棧以前,簡單介紹一個概念,棧幀
棧幀:一個棧幀隨着一個方法的調用開始而建立,這個方法調用完成而銷燬。棧幀內存放者方法中的局部變量,操做數棧等數據。
JVM棧只對棧幀進行存儲,壓棧和出棧操做。棧內存的大小能夠有兩種設置,固定值和根據線程須要動態增加。在JVM棧這個數據區可能會發生拋出兩種錯誤。
StackOverflowError 出如今棧內存設置成固定值的時候,當程序執行須要的棧內存超過設定的固定值會拋出這個錯誤。
OutOfMemoryError 出如今棧內存設置成動態增加的時候,當JVM嘗試申請的內存大小超過了其可用內存時會拋出這個錯誤。
一個支持native方法調用的JVM實現,須要有這樣一個數據區,就是本地方法棧,Java官方對於本地方法的定義爲methods written in a language other than the Java programming language,就是使用非Java語言實現的方法,可是一般咱們指的通常爲C或者C++,所以這個棧也有着C棧這一稱號。一個不支持本地方法執行的JVM沒有必要實現這個數據區域。本地方法棧基本和JVM棧同樣,其大小也是能夠設置爲固定值或者動態增長,所以也會對應拋出StackOverflowError和OutOfMemoryError錯誤。
屬於這一種的數據區包含 堆內存,方法區和運行時常量池。這些數據區能夠被每個線程訪問,他們隨着JVM啓動而初始化,同時伴隨JVM關閉而銷燬。
堆數據區是用來存放對象和數組(特殊的對象)。堆內存由多個線程共享。堆內存隨着JVM啓動而建立。衆所周知,Java中有一個很好的特性就是自動垃圾回收。垃圾回收就操做這個數據區來回收對象進而釋放內存。若是堆內存剩餘的內存不足以知足於對象建立,JVM會拋出OutOfMemoryError錯誤。
在JVM規範中,方法區被視爲堆內存的一個邏輯部分。這一點可能因爲具體的JVM實現而不一樣,甚至在方法區不實現垃圾回收處理也是能夠的。方法區和堆內存同樣被多個線程訪問,方法區中存放類的信息,好比類加載器引用,屬性,方法代碼和構造方法和常量等。當方法區的可用內存沒法知足內存分配需求時,JVM會拋出OutOfMemoryError錯誤。
運行時常量池建立在方法區,當一個類或者一個接口被建立的時候,JVM會建立一個運行時常量池。一個運行時常量池其實是一個類或者接口的class文件中常量池表(constant_pool table)的運行時展現形式。一個運行時常量池包含了多種類型的常量,從諸如運行時能夠肯定的數值型字面量到運行時才能決定的方法和屬性引用。當運行時常量池沒法知足於內存分配需求時,JVM會拋出OutOfMemoryError錯誤。
當一我的開始學習Java或者其餘編程語言的時候,會接觸到堆和棧,因爲一開始沒有明確清晰的說明解釋,不少人會產生不少疑問,什麼是堆,什麼是棧,堆和棧有什麼區別?更糟糕的是,Java中存在棧這樣一個後進先出(Last In First Out)的順序的數據結構,這就是java.util.Stack。這種狀況下,難免讓不少人更加費解前面的問題。事實上,堆和棧都是內存中的一部分,有着不一樣的做用,並且一個程序須要在這片區域上分配內存。衆所周知,全部的Java程序都運行在JVM虛擬機內部,咱們這裏介紹的天然是JVM(虛擬)內存中的堆和棧。
最主要的區別就是棧內存用來存儲局部變量和方法調用。
而堆內存用來存儲Java中的對象。不管是成員變量,局部變量,仍是類變量,它們指向的對象都存儲在堆內存中。
棧內存歸屬於單個線程,每一個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存能夠理解成線程的私有內存。
而堆內存中的對象對全部線程可見。堆內存中的對象能夠被全部線程訪問。
若是棧內存沒有可用的空間存儲方法調用和局部變量,JVM會拋出java.lang.StackOverFlowError。
而若是是堆內存沒有可用的空間存儲生成的對象,JVM會拋出java.lang.OutOfMemoryError。
棧的內存要遠遠小於堆內存,若是你使用遞歸的話,那麼你的棧很快就會充滿。若是遞歸沒有及時跳出,極可能發生StackOverFlowError問題。
你能夠經過-Xss選項設置棧內存的大小。-Xms選項能夠設置堆的開始時的大小,-Xmx選項能夠設置堆的最大值。
這就是Java中堆和棧的區別。理解好這個問題的話,能夠對你解決開發中的問題,分析堆內存和棧內存使用,甚至性能調優都有幫助。
查看堆的默認值,使用下面的代碼,其中InitialHeapSize爲最開始的堆的大小,MaxHeapSize爲堆的最大值。
13:17 $ java -XX:+PrintFlagsFinal -version | grep HeapSize uintx ErgoHeapSizeLimit = 0 {product} uintx HeapSizePerGCThread = 87241520 {product} uintx InitialHeapSize := 134217728 {product} uintx LargePageHeapSizeThreshold = 134217728 {product} uintx MaxHeapSize := 2147483648 {product} java version "1.8.0_25" Java(TM) SE Runtime Environment (build 1.8.0_25-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
查看棧的默認值,其中ThreadStackSize爲棧內存的大小。
13:21 $ java -XX:+PrintFlagsFinal -version | grep ThreadStackSize intx CompilerThreadStackSize = 0 {pd product} intx ThreadStackSize = 1024 {pd product} intx VMThreadStackSize = 1024 {pd product} java version "1.8.0_25" Java(TM) SE Runtime Environment (build 1.8.0_25-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
[3] JVM的相關知識整理和學習
http://www.importnew.com/16388.html
[4] JVM 之 運行時數據區(更新)
http://my.oschina.net/HeliosFly/blog/357922
[5] internal architecture of the Java Virtual Machine (JVM)
http://blog.jamesdbloom.com/JVMInternals.html
[6] Java內存管理原理及內存區域詳解
http://www.importnew.com/16433.html
[7] Java中的堆和棧的區別
http://javarevisited.blogspot.com.au/2013/01/difference-between-stack-and-heap-java.html
http://droidyue.com/blog/2014/12/07/differences-between-stack-and-heap-in-java/
[8] JVM學習筆記
http://blog.brucefeng.info/post/jvm-notes
http://7xkbey.com1.z0.glb.clouddn.com/JVM.png
[9] JVM的內存區域劃分
http://www.importnew.com/18961.html
[10] 多是把Java內存區域講的最清楚的一篇文章
http://www.javashuo.com/article/p-rprocxuf-ew.html
[11] JVM,我要把你 「開膛破肚」 !