Java虛擬機(1)-JVM內存模型

1. JVM(Hotspot)內存模型簡介

這裏寫圖片描述

如圖所示,JVM的內存模型中主要涵蓋了以下5個部分.


1.1 程序計數器

程序計數器主要存儲每個線程執行的字節碼指令的行號.
線程私有.
Java方法中的代碼經過javac處理後,在class文件中以Code屬性表形式存在,表中以數值方式存儲了相應的字節碼指令.
當線程執行native方法時,存儲的值爲undefined.該區域不會出現OOM.


1.2 虛擬機棧

VM棧中以棧幀的形式存儲了線程執行Java方法的信息.VM棧只有棧幀的入棧和出棧兩種動作.
線程私有.


1.3 本地方法棧

Native棧的作用和VM棧類似,當線程執行了非Java方法(如C實現)時,會在內存中開闢一份Native棧,用以存儲改方法的執行信息.
線程私有.
Hotspot中,VM棧和Native棧已經合二爲一.


1.4 方法區

方法區用以儲存常量,靜態變量,類的符號引用等信息.
各個線程公有.
Hotspot中將垃圾回收分代的思想引申到了方法區,所以Hotspot中的方法區也就是持久帶.


1.5 堆

堆內存的唯一作用就是存儲對象實例.
各線程公有.


2. 代碼演示內存溢出異常


2.1 堆溢出

    -
public class JVMTest {
    static class OOMTest {
    };

    public static void main(String[] args) {
        /**
         * 模擬堆溢出
         */
        List<OOMTest> list = new ArrayList<OOMTest>();
        while (true) {
            list.add(new OOMTest());
        }
    }
}

對象創建過多導致堆內存不足.


2.2 方法區溢出

public class JVMTest {
    static class OOMTest {
    };

    public static void main(String[] args) {
         /**
         * 模擬方法區溢出
         */
         List<String> list = new ArrayList<String>();
         int i=0;
         while(true){
         list.add(String.valueOf(i++).intern());
         //String類的intern方法創建的字符串,不會在編譯期而是在運行期放入方法區的常量池
         }
    }
}

常量池中字符串過多導致方法區內存不足.


2.3 VM棧溢出

public class JVMTest3 {
    private int count = 1;

    public void stackTest() {
        count++;
        stackTest();
    }

    public static void main(String[] args) throws Throwable {
        JVMTest3 test = new JVMTest3();
        try {
            test.stackTest();
        } catch (Throwable err) {
            System.out.println("Stack length:" + test.count);
            throw err;
        }
    }
}

方法調用層次過多導致棧深過大.


3.其他


  • 什麼是JIT即時編譯

    Java的編譯通常指由源文件編譯成字節碼的過程.
    但是,字節碼是可以在不同平臺運行的,由字節碼指令轉化到機器指令的過程需要運行時進行.


  • trictfp關鍵字

    此關鍵字可以修飾類,接口,方法.
    被strictfp修飾後,內部的所有運算嚴格遵守IEEE754規範,不會應爲運行在不同平臺上產生不同的結果.


  • 各版本jdk的一些新特性

    Jdk1.4:
    真正走向成熟的版本.
    Jdk1.5
    增加了泛型,枚舉等特性.
    Java.util.concurrent併發包等.
    Jdk1.6:
    在同步鎖,垃圾收集等方面做了一些改進.
    Jdk1.7:
    提供了G1收集器
    Jdk1.8:
    G1收集器爲jvm的默認收集器.


  • 什麼是常量池,存在於內存哪個部分,存儲什麼

    class文件常量池:存放編譯器生成的字面量和符號引用
    運行時常量池:存儲運行期生成的常量,如String.intern().
    都屬於方法區.


  • 查看class文件中常量池信息

    javap -verbose ../JVMTest4.class


  • 什麼是直接內存

    使用native函數庫直接分配的對外內存,與NIO類相關.


  • 常見JVM的設置參數有哪些

    -Xms 初始堆大小
    -Xmx 最大堆大小
    -Xmn 年輕代大小
    -XX:PermSize 初始永久代大小,默認物理內存1/64
    -XX:MaxPermSize 最大永久代大小,默認物理內存1/4
    -Xss 單線程棧大小,棧深不是很大128k足夠.


  • 永久代與方法區的區別

    永久代只是hotspot將分代回收的理念引申到了方法區,
    且在jdk1.8之後,不存在永久代的概念,用metaspace實現了方法區.


  • jdk1.6,1.7,1.8中方法區的遷移過程,String.intern()在1.6和1.7中有什麼不同

    1.6中,使用永久代實現了方法區
    1.7中,將運行時常量池從方法區移至堆中.
    1.8中,不存在永久代,使用metaspace實現了方法區.
    如以下代碼:

String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

Jdk1.6:
1)常量池中創建”1”對象,堆中兩個new String()對象,存儲常量池中”1”的引用.堆中創建相加後的對象(內容爲”11”),s3存儲這個對象的引用.
2)s3.intern()執行時,去常量池中尋找”11”,沒找到則在常量池中創建”11”.
3)S4在常量池中找到”11”,返回引用.
4)所以,結果爲false.

Jdk1.7
1)常量池中創建”1”對象,堆中兩個new String()對象,存儲常量池中”1”的引用.堆中創建相加後的對象(內容爲”11”),s3存儲這個對象的引用
2)s3.intern()執行時,去常量池中尋找”11”,沒找到,則將堆中相加後對象的引用存儲在常量池中.
3)S4獲取到的爲對中內容爲”11”的引用,和s3的內容一致
4)結果爲true


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