天天看點

深入了解Java虛拟機-記憶體區域

java虛拟機執行Java程式把管理的記憶體分為若幹個不同的資料區域。各區域有各的用途,建立銷毀時間,有些區域随着虛拟機程序啟動而存在,有些區域是依賴使用者線程的啟動和結束而建立和銷毀。java虛拟機管理的記憶體區域包括以下幾個運作時資料區,如下圖

1、程式計數器(program counter register)

較小的記憶體空間,目前線程所執行的位元組碼的行号訓示器。位元組碼解釋器工作時就是通過改為這個計數器的值來選取下一條需要執行的位元組碼指令,分支、循環、跳轉、異常處理、線程恢複等基礎功能都需要依賴這個計數器來完成。

java虛拟機的多線程是線程輪流切換CPU時間片來實作,任何時刻,一個處理器隻能執行一個線程的指令。線程切換後恢複到執行位置,每個線程需要一個獨立計數器,線程私有。

java方法,計數器是虛拟機位元組碼位址;native方法,計數器為空,電腦必須要容納方法的傳回位址或者指針。該區域唯一一個沒有規定OutOfMemoryError的區域。

2、虛拟機棧

線程私有,聲明周期與線程相同。虛拟機棧描述的是java方法執行的記憶體模型,每個方法執行建立一個棧幀存儲操作數棧、動态連結、局部變量表等資訊。每個方法調用到完成相當于虛拟機棧中的入棧與出棧。

固定大小,也可擴充與收縮。固定大小需要獨立建立。動态擴充與收縮提供最大與最小。

規定兩種異常:棧深度超過虛拟機的深度報StackOverflowError;擴充記憶體時,棧無法申請到記憶體,報OutOfMemoryError錯誤

3、本地方法棧

與虛拟機棧相似,也抛兩個異常。對本地方法的使用語言使用方式與資料結構沒有規定。

4、java堆

記憶體最大,線程共享,幾乎存放所有對象執行個體以及記憶體配置設定。所有類的執行個體和數組對象都要在堆中配置設定。

java堆是垃圾收集器的主要區域,也稱GC堆。收集器按分代收集算法,細分為新生代和老年代,新生代又分Eden空間、From Survivor、To Survivor空間。線程共享的java堆中可能劃分多個線程私有的配置設定緩沖區。反正存放執行個體,再分為了更快回收與配置設定。

java堆可以使實體不連續,邏輯上連續,可固定大小,可擴充與收縮 (-Xms與-Xms控制),記憶體不足報OutOfMemory異常。

5、方法區

線程共享區域,存儲加載的類資訊、常量、靜态變量、即時編譯器編譯後的代買資料。虛拟機啟動時方法被建立。

HotSpot虛拟機方法區被稱為永久代,永久代不等價方法區,把GC分代收集擴充到方法區,jdk8移除永久代,用本地記憶體代替。

不要連續記憶體,可以擴充記憶體,可以選擇不垃圾收集(垃圾收集不理想),記憶體不足時報OutOfMemory異常。

6、運作時常量時

方法區的一部分。class方法中除了有類的版本、字段、方法、接口等描述資訊外,還有常量表,存儲編譯期生成的各種字面量和符号引用。

java常量不一定隻在編譯器産生,程式也可以新添加,如String類的intern()方法。

常量池記憶體不足報OutOfMemory異常。

7、直接記憶體

不是運作時資料區的一部分,也不是java虛拟機規範定義的記憶體區域,但這部分記憶體頻繁使用,也可能導緻OutOfMemoryError錯誤出現。垃圾收集時,會對直接記憶體收集,但不會像新生代和老年代那樣,發現不足就觸發收集,它隻能等到老年代滿了後FullGC時,順便清理掉直接記憶體中廢棄的對象。

jdk1.4加入NIO,引入基于通道(channel)與緩沖區(Buffer)的I/O方法,它可以直接使用Native函數直接配置設定堆外記憶體,通過java堆中DirectByteBuffer對象作為這塊記憶體的引用進行操作。提高一些場景性能,避免java堆與native堆中來回複制資料。