JVM內存模型能夠分爲兩個部分,以下圖所示,堆和方法區是全部線程共有的,而虛擬機棧,本地方法棧和程序計數器則是線程私有的。下面咱們就來一一分析一下這些不一樣區域的做用。java
堆內存是全部線程共有的,能夠分爲兩個部分:年輕代和老年代。下圖中的Perm表明的是永久代,可是注意永久代並不屬於堆內存中的一部分,同時jdk1.8以後永久代也將被移除。github
GC(垃圾回收器)對年輕代中的對象進行回收被稱爲Minor GC,用通俗一點的話說年輕代就是用來存放的年輕的對象,年輕對象是什麼意思呢?年輕對象能夠簡單的理解爲沒有經歷過屢次垃圾回收的對象,若是一個對象經歷過了必定次數的Minor GC,JVM通常就會將這個對象放入到年老代,而JVM對年老代的對象的回收則稱爲Major GC。算法
如上圖所示,年輕代中還能夠細分爲三個部分,咱們須要重點關注這幾點:jvm
import java.util.ArrayList; import java.util.List; /* java -Xms20m -Xmx20m HeapOOM */ 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 at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:261) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) at java.util.ArrayList.add(ArrayList.java:458) at HeapOOM.main(HeapOOM.java:13)
堆內存是咱們平時在生產環境中進行性能調優中的一個很是重要的部分,對於這裏我在另一篇文章JVM垃圾回收算法及回收器詳解有詳細介紹,這裏咱們仍是拓展補充幾個常見的性能調優參數:優化
方法區與Java堆同樣,是各個線程共享的區域,它用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯(JIT)後的代碼等數據。spa
對於JDK1.8以前的HotSpot虛擬機而言,不少人常常將方法區稱爲咱們上圖中所描述的永久代,實際上二者並不等價,由於這僅僅是HotSpot的設計團隊選擇利用永久代來實現方法區而言。同時對於其餘虛擬機好比IBM J9中是不存在永久代的概念的。操作系統
其實,移除永久代的工做從JDK1.7就開始了。JDK1.7中,存儲在永久代的部分數據就已經轉移到了Java Heap或者是 Native Heap。但永久代仍存在於JDK1.7中,並沒徹底移除,譬如符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變量(class statics)轉移到了java heap,而在JDK1.8以後永久代的該概念也已經再也不存在取而代之的是元空間metaspace。線程
常量池實際上是方法區中的一部分,由於這裏比較重要,因此咱們拿出來單獨看一下。注意咱們這裏所說的運行時的常量池並僅僅是指Class文件中的常量池,由於JVM可能會進行即時編譯進行優化,在運行時將部分常量載入到常量池中。
JVM中的程序計數器和計算機組成原理中提到的程序計數器PC概念相似,是線程私有的,用來記錄當前執行的字節碼位置。仍是稍微解釋一下吧,CPU的佔有時間是以分片的形式分配給給每一個不一樣線程的,從操做系統的角度來說,在不一樣線程之間切換的時候就是依賴程序計數器來記錄上一次線程所執行到具體的代碼的行數,在JVM就是字節碼。
與程序計數器同樣,Java虛擬機棧也是線程私有的,用通俗的話將它就是咱們經常據說到堆棧中的那個「棧內存」。虛擬機棧描述的是Java方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀(Stack Frame)用於存儲局部變量表(局部變量表須要的內存在編譯期間就肯定了因此在方法運行期間不會改變大小),操做數棧,動態連接,方法出口等信息。每個方法從調用至出棧的過程,就對應着棧幀在虛擬機中從入棧到出棧的過程。p.s: 關於棧幀這裏咱們之後講虛擬機字節碼執行引擎的時候再來仔細分析。
本地方法棧和Java虛擬機棧相似,只不過是爲JVM執行Native方法服務,這裏就不解釋了。
GitHub: https://github.com/ziwenxie
Blog: https://www.ziwenxie.site
本文爲做者原創,轉載請於開頭明顯處聲明我的博客出處 :-)