jvm內存模型,java類從編譯到加載到執行的過程,jvm內存分配過程

1、jvm內存模型html

JVM 內存模型主要分爲堆、程序計數器、方法區、虛擬機棧和本地方法棧java

一、堆數組

1.一、堆是 JVM 內存中最大的一塊內存空間。安全

1.二、該內存被全部線程共享,幾乎全部對象和數組都被分配到了堆內存中。多線程

1.三、堆被劃分爲新生代和老年代,新生代又被進一步劃分爲 Eden 和 Survivor 區,最後 Survivor 由 From Survivor 和 To Survivor 組成。jvm

二、程序計數器(Program Counter Register)函數

程序計數器是一塊很小的內存空間,用來記錄下一條運行的指令(實際是記錄各個線程執行的字節碼的地址,因爲 Java 是多線程語言,當執行的線程數量超過 CPU 核數時,線程之間會根據時間片輪詢爭奪 CPU 資源。若是一個線程的時間片用完了,或者是其它緣由致使這個線程的 CPU 資源被提早搶奪,那麼這個退出的線程就須要單獨的一個程序計數器,來記錄下一條運行的指令。),例如,分支、循環、跳轉、異常、線程恢復等都依賴於計數器。工具

三、方法區(Method Area)spa

3.一、方法區!=永久代,根據虛擬機類型而定;HotSpot 虛擬機使用永久代來實現方法區,但在其它虛擬機中,例如,Oracle 的 JRockit、IBM 的 J9 就不存在永久代一說。操作系統

3.二、方法區主要是用來存放已被虛擬機加載的類相關信息,包括類信息(類的版本、字段、方法、接口和父類等信息)、運行時常量池、字符串常量。

ps1:JVM 在執行某個類的時候,必須通過加載、鏈接、初始化,而鏈接又包括驗證、準備、解析三個階段。在加載類的時候,JVM 會先加載 class 文件,而在 class 文件中除了有類的版本、字段、方法和接口等描述信息外,還有一項信息是常量池 (Constant Pool Table),用於存放編譯期間生成的各類字面量和符號引用()。

ps2:字面量包括字符串(String a=「b」)、基本類型的常量(final 修飾的變量)。

ps3:符號引用則包括類和方法的全限定名(例如 String 這個類,它的全限定名就是 Java/lang/String)、變量的名稱和描述符以及方法的名稱和描述符。

ps4:當類加載到內存中後,JVM 就會將 class 文件常量池中的內容存放到運行時的常量池中。

ps5:在解析階段,JVM 會把符號引用替換爲直接引用(對象的索引值)。

3.三、方法區與堆空間相似,也是一個共享內存區,因此方法區是線程共享的。

3.二、在 Java6 版本中,永久代在非堆內存區;到了 Java7 版本,永久代的靜態變量和運行時常量池被合併到了堆中;而到了 Java8,永久代被元空間取代了,而且元空間的存儲位置是本地內存,永久代的靜態變量(class static variables)以及運行時常量池(runtime constant pool)則跟 Java7 同樣,轉移到了堆中。以下圖:

ps1:java8用直接內存元空間替代永久代的好處

1-一、移除永久代是爲了融合 HotSpot JVM 與 JRockit VM 而作出的努力,由於 JRockit 沒有永久代,因此不須要配置永久代。

1-二、永久代內存常常不夠用或發生內存溢出,爆出異常 java.lang.OutOfMemoryError: PermGen。這是由於在 JDK1.7 版本中,指定的 PermGen 區大小爲 8M,因爲 PermGen 中類的元數據信息在每次 FullGC 的時候均可能被收集,回收率都偏低,成績很難使人滿意;還有,爲 PermGen 分配多大的空間很難肯定,PermSize 的大小依賴於不少因素,好比,JVM 加載的 class 總數、常量池的大小和方法的大小等。

四、虛擬機棧(VM stack)

4.一、Java 虛擬機棧是線程私有的內存空間,它和 Java 線程一塊兒建立。

4.二、用來保存方法的局部變量、操做數棧、動態連接方法和返回地址等信息,並參與方法的調用和返回。每個方法的調用都伴隨着棧幀的入棧操做,方法的返回則是棧幀的出棧操做。

五、本地方法棧(Native Method Stack)

5.一、本地方法棧跟 Java 虛擬機棧的功能相似,Java 虛擬機棧用於管理 Java 函數的調用,而本地方法棧則用於管理本地方法的調用。

5.二、本地方法並非用 Java 實現的,而是由 C 語言實現的。

 

2、java類從編譯到加載到執行的過程

一、編譯

經過jdk自帶的javac工具完成

二、類加載

2.一、當一個類被建立實例或者被其它對象引用時,虛擬機在沒有加載過該類的狀況下,會經過類加載器將字節碼文件加載到內存中。

2.二、不一樣的實現類由不一樣的類加載器加載,JDK 中的本地方法類通常由根加載器(Bootstrp loader)加載進來,JDK 中內部實現的擴展類通常由擴展加載器(ExtClassLoader )實現加載,而程序中的類文件則由系統加載器(AppClassLoader )實現加載。

2.三、在類加載後,class 類文件中的常量池信息以及其它數據會被保存到 JVM 內存的方法區中。

三、類鏈接

類在加載進來以後,會進行鏈接、初始化,最後纔會被使用。在鏈接過程當中,又包括驗證、準備和解析三個部分。

3.一、驗證

驗證類符合 Java 規範和 JVM 規範,在保證符合規範的前提下,避免危害虛擬機安全。

3.二、準備

爲類的靜態變量分配內存,初始化爲系統的初始值。對於 final static 修飾的變量,直接賦值爲用戶的定義值。例如,private final static int value=123,會在準備階段分配內存,並初始化值爲 123,而若是是 private static int value=123,這個階段 value 的值仍然爲 0。

3.三、解析

將符號引用轉爲直接引用的過程。咱們知道,在編譯時,Java 類並不知道所引用的類的實際地址,所以只能使用符號引用來代替。類結構文件的常量池中存儲了符號引用,包括類和接口的全限定名、類引用、方法引用以及成員變量引用等。若是要使用這些類和方法,就須要把它們轉化爲 JVM 能夠直接獲取的內存地址或指針,即直接引用。

3.四、類初始化

編譯器會在將 .java 文件編譯成 .class 文件時,收集全部類初始化代碼,包括靜態變量賦值語句、靜態代碼塊、靜態方法,收集在一塊兒成爲 <clinit>() 方法。在這個階段中,JVM 首先將執行構造器 <clinit> 方法

初始化類的靜態變量和靜態代碼塊爲用戶自定義的值,初始化的順序和 Java 源碼從上到下的順序一致。例如:

private static int i=1;
static{
  i=0;
}
public static void main(String [] args){
  System.out.println(i);
}

運行結果爲:0

子類初始化時會首先調用父類的 <clinit>() 方法,再執行子類的 <clinit>() 方法,運行如下代碼:public class Parent{

public static String parentStr= "parent static string"; static{ System.out.println("parent static fields"); System.out.println(parentStr); } public Parent(){ System.out.println("parent instance initialization"); } } public class Sub extends Parent{ public static String subStr= "sub static string"; static{ System.out.println("sub static fields"); System.out.println(subStr); } public Sub(){ System.out.println("sub instance initialization"); } public static void main(String[] args){ System.out.println("sub main"); new Sub(); } }


運行結果爲:

parent static fields
parent static string
sub static fields
sub static string
sub main
parent instance initialization
sub instance initialization

JVM 會保證 <clinit>() 方法的線程安全,保證同一時間只有一個線程執行。

JVM 在初始化執行代碼時,若是實例化一個新對象,會調用 <init> 方法對實例變量進行初始化,並執行對應的構造方法內的代碼。

類初始化完後就能夠正式使用了,好比建立對象等。

四、運行時編譯

見:http://www.javashuo.com/article/p-rhnsmsau-cu.html

 

3、jvm內存分配過程

一、JVM 根據配置配置或默認參數向操做系統申請內存空間

二、JVM 得到內存空間後,會根據配置參數分配堆、棧以及方法區的內存大小

三、class 文件加載、驗證、準備以及解析,其中準備階段會爲類的靜態變量分配內存,初始化爲系統的初始值

四、使用過程當中內存分配

相關文章
相關標籤/搜索