JAVA內存結構解析

原文鏈接:http://tryenough.com/java-memory-structure

更多:http://tryenough.com/type-android

Java內存結構大體示意圖:

程序計數器

程序計數器(Program Counter Register)是JVM中一塊較小的內存區域,保存着當前線程執行的虛擬機字節碼指令的內存地址。Java多線程的實現,其實是通過線程間的輪流切換並分配處理器執行時間的方式來實現的,在任何時刻,處理器都只會執行一個線程中的指令。在多線程場景下,爲了保證線程切換回來後,還能恢復到原先狀態,找到原先執行的指令,所以每個線程都會設立一個程序計數器,並且各個線程之間不會互相影響,程序計數器爲"線程私有"的內存區域。

如果當前線程正在執行Java方法,則程序計數器保存的是虛擬機字節碼的內存地址,如果正在執行的是Native方法(非Java方法,JVM底層有許多非Java編寫的函數實現),計數器則爲空。程序計數器是唯一一個在Java規範中沒有規定任何OutOfMemory場景的區域。

虛擬機棧

虛擬機棧(Java Virtual Machine Stacks)和線程是緊密聯繫的,每創建一個線程時就會對應創建一個Java棧,所以Java棧也是"線程私有"的內存區域,這個棧中又會對應包含多個棧幀,每調用一個方法時就會往棧中創建並壓入一個棧幀,棧幀是用來存儲方法數據和部分過程結果的數據結構,每一個方法從調用到最終返回結果的過程,就對應一個棧幀從入棧到出棧的過程。

虛擬機棧是一個後入先出的數據結構,線程運行過程中,只有一個棧幀是處於活躍狀態的,被稱爲"當前活動幀棧",當前活動幀棧始終是虛擬機棧的棧頂元素。

原文鏈接:http://tryenough.com/java-memory-structure

更多:http://tryenough.com/type-android

本地方法棧

本地方法棧(Native Method Stack)和虛擬機棧的作用相似,不過虛擬機棧是爲Java方法服務的,而本地方法棧是爲Native方法服務的。

方法區

方法區(Method Area)是用於存儲類結構信息的地方,包括常量池、靜態變量、構造函數等類型信息,類型信息是由類加載器在類加載時從類文件中提取出來的。

方法區同樣存在垃圾收集,因爲用戶通過自定義加載器加載的一些類同樣會成爲垃圾,JVM會回收一個未被引用類所佔的空間,以使方法區的空間達到最小。

方法區中還存在着常量池,常量池包含着一些常量和符號引用(加載類的連接階段中的解析過程會將符號引用轉換爲直接引用)。

方法區是線程共享的。


  堆(heap)是存儲java實例或者對象的地方,是GC的主要區域,同樣是線程共享的內存區域。

實例分析:

public class JVMTest {//類加載時將類信息放在方法區內,堆內生成java.lang.class對象,持有指向方法區該類的引用
    
    /*
     * i1,i2都爲Integer對象的引用,線程運行時放在java棧中新建的幀棧中
     * 該兩個引用執行堆中的Integer實例的引用,而兩實例在堆中的地址是不同的,所以,i1==i2返回false
     */
    static Integer i1=new Integer(1);
    static Integer i2=new Integer(1);
    
    /*
     * a,b引用同樣放在java棧中幀棧中,指向常量池中的同一內存空間,所以,a==b返回true
     */
    static Integer a=1;
    static Integer b=1;
    
    public static void main(String[] args){//main方法放入方法區
        /**
         * stu是對student對象的引用,放入棧中,指向堆中對象的內存地址。
         * new出來的student對象放在堆中,並持有方法區中student類型信息的引用
         */
        Student stu=new Student("jialimin");
        /**
         * 執行add方法時,根據java棧中stu定位到堆中的對象實例,再根據堆中持有的位於方法區的student類型信息,獲得add()
         * 字節碼,執行此方法執行,打印出結果
         */
        stu.add();
    }
}

class Student{
    public String name;
    public Student(String name){
        this.name=name;
    }
    public void add(){
        System.out.println(name);
    }
}

原文鏈接:http://tryenough.com/java-memory-structure

更多:http://tryenough.com/type-android

上面main方法中運行的程序過程如下:

(1)用戶創建了一個Student對象,運行時JVM首先會去方法區尋找該對象的類型信息,沒有則使用類加載器classloader將Student.class字節碼文件加載至內存中的方法區,並將Student類的類型信息存放至方法區。

(2)接着JVM在堆中爲新的Student實例分配內存空間,這個實例持有着指向方法區的Student類型信息的引用,引用指的是類型信息在方法區中的內存地址。

(3)在此運行的JVM進程中,會首先起一個線程跑該用戶程序,而創建線程的同時也創建了一個虛擬機棧,虛擬機棧用來跟蹤線程運行中的一系列方法調用的過程,每調用一個方法就會創建並往棧中壓入一個棧幀,棧幀用來存儲方法的參數,局部變量和運算過程的臨時數據。上面程序中的stu是對Student的引用,就存放於棧中,並持有指向堆中Student實例的內存地址。

(4)JVM根據stu引用持有的堆中對象的內存地址,定位到堆中的Student實例,由於堆中實例持有指向方法區的Student類型信息的引用,從而獲得add()方法的字節碼信息,接着執行add()方法包含的指令。

原文鏈接:http://tryenough.com/java-memory-structure

更多:http://tryenough.com/type-android

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章