若是想了解JVM內存模型,首先咱們要知道JVM是什麼?JVM全稱 Java Virtual Machine ,即Java虛擬機,是用於運行Java程序編譯後的字節碼文件。java
JVM最多見的三種有:
1.Sun公司的 HotSpot,是目前使用最普遍的Java虛擬機。
2.BEA公司的 JRockit,後來被 Oracle收購。
3.IBM公司的 J9VM。segmentfault
咱們知道,Java的口號是: 「Write once, run anywhere」,即一次編寫,處處運行。爲何能夠作到這樣呢,其實就是依賴於JVM。在不一樣的操做系統上,只要安裝了對應的虛擬機,那麼一樣的一份代碼,就能夠隨意移植。數組
當編寫完Java代碼時,即產生 .Java文件,會經過Java編譯器編譯爲.class 文件,而後經過Class Loader把類信息加載到JVM中,最後JVM再去調用操做系統。這樣,只要JVM正確執行.class文件,就能夠實現跨平臺了。spa
如下即爲JVM的內存模型圖:操作系統
程序計數器:線程
程序計數器是一塊較小的內存,能夠看作是當前線程所執行的字節碼的行號指示器,即記錄當前線程所執行到的字節碼的行號。當字節碼解釋器工做時,就是經過改變計數器的值來選取下一條須要執行的字節碼指令。由此來完成分支、循環、跳轉、線程恢復、異常處理等功能。3d
程序計數器是線程私有的(即每一個線程擁有一個程序計數器),各個線程之間的程序計數器互不干擾。程序計數器的生命週期跟隨線程的生命週期,若線程消亡,則程序計數器也會消亡。對象
若是一個線程正在執行的是Java方法,則程序計數器記錄的是正在執行的字節碼指令的地址;若是正在執行的是 native 本地方法,則程序計數器記錄的是 Undefined .blog
棧接口
指的是Java虛擬機棧,它也是線程私有的,所以生命週期和線程相同。每當線程建立的時候,都會建立一個私有的Java虛擬機棧。Java棧中保存了局部變量和方法參數等,同時和Java方法的調用、返回密切相關。
每一個方法在執行的同時都會建立一個棧幀,用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每個方法從調用到執行完成的過程,就對應一個棧幀在虛擬機棧中從入棧到出棧的過程。
本地方法棧
本地方法棧和Java虛擬機棧很是相似,它們最大的不一樣在於,Java虛擬機棧用於Java方法的調用,而本地方法棧用於Native本地方法的調用。
堆
Java堆是全部線程共享的一塊內存區域,在虛擬機啓動時建立。對於絕大多數應用來講,Java堆是JVM所管理的內存中最大的一塊,幾乎全部的對象實例和數組都存放在這裏。
Java堆也是垃圾收集器管理的主要區域。堆中分爲新生代、老年代和永久代,新生代還可細分爲Eden區、From、To 區。當堆中沒有內存可分配時,就會拋出OOM異常。
方法區
方法區同Java堆同樣,也是全部線程共享的內存區域。用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。在JDK8之前,HotSpot是用「永久代」來實現方法區的,其餘虛擬機(如JRockit、J9VM)不存在永久代這個概念。這樣的話,方法區能夠和Java堆同樣被 HotSpot的垃圾收集器所管理,不須要單獨處理。
因爲咱們能夠經過 -XX:MaxPermSize 來設置永久代大小,所以若使用永久代來實現方法區,則會有內存溢出的風險。所以,在JDk8中,取消了永久代,用元空間代替之。也就是說,用元空間來實現方法區。
元空間的本質和永久代相似,都是對JVM規範中方法區的實現。元空間與永久代之間最大的區別在於:永久代是堆的一部分,和新生代,老年代地址是連續的。元空間並不在虛擬機中,而屬於 Native Memeory(本地內存)。所以,默認狀況下,元空間的大小僅受本地內存限制。
運行時常量池
首先須要知道常量池和運行時常量池的區別。
常量池,即指class文件常量池,是class文件的一部分。java文件被編譯成class文件以後,除了包含了類的版本、字段、方法、接口等描述信息,還有一項信息叫作class文件常量池。其用於存放編譯期生成的各類字面量和符號引用。
運行時常量池是方法區的一部分。當類加載到內存中,JVM就會將class文件常量池中的內容(字面量和符號引用)存放到運行時常量池中。
Java並不要求常量必定只有在編譯期才能夠產生,在運行期間也能夠產生新的常量並放入池中。
直接內存
Java的NIO庫容許Java程序使用直接內存。直接內存是Java堆外的,直接向系統申請的一塊內存空間(直接內存不屬於虛擬機運行時數據區)。所以,直接內存的大小不受虛擬機的限制,只受本機內存的限制。一般訪問直接內存的速度會快於訪問堆的速度。