JVM——java內存區域

原文地址:http://www.mamicode.com/info-detail-2140204.html

java虛擬機內存管理

線程獨佔區之程序計數器

程序計數器是一塊較小的內存空間,它可以看做是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。

如果線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是Native方法,這個計數器值則爲空(Undefined)。此內存區域是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域

線程獨佔區之Java虛擬機棧

  • Java虛擬機棧與線程生命週期相同。其描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲局部變量表操作數棧、動態鏈接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。
  • 局部變量表存放了編譯器可知的各種基本數據類型(boolean, byte, char, short, int, float, long, double),對象引用(reference類型)和returnAddress類型(指向了一條字節碼指令的地址)。
  • 在這個區域中,Java虛擬機規範規定了兩種異常情況:

    1. 如果線程請求的棧深度大於虛擬機所允許的深度,拋出StackOverflowError異常;
    2. 如果虛擬機棧可以動態擴展(當前大部分Java虛擬機都可動態擴展,只不過Java虛擬機規範中也允許固定長度的虛擬機棧),並且擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。

線程獨佔區之本地方法棧

本地方法棧與虛擬機棧所發揮的作用非常相似,區別是:虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,本地方法棧爲虛擬機使用到的Native方法服務。

由於虛擬機規範中沒有對本地方法棧中的語言、使用方式與數據結構進行強制規定,有的虛擬機(如Sun HotSpot虛擬機)直接把本地方法棧和虛擬機棧合二爲一。

本地方法棧也會拋出StackOverflowError異常和OutOfMemoryError異常。

線程共享區之Java堆

Java堆是Java虛擬機所管理的內存中最大的一塊,在虛擬機啓動時創建,其唯一目的就是存放對象實例:所有的對象實例以及數組都要在堆上分配(但隨着JIT編譯器的發展與逃逸分析技術逐漸成熟,所有的對象分配在堆上也漸漸不是那麼絕對了)。

Java堆是垃圾收集器管理的主要區域,現在收集器基本採用分代收集算法,所以Java堆還可細分爲:新生代老年代;再細緻點分爲Eden空間From Survivor空間To Survivor空間等。

根據Java虛擬機規範的規定,Java堆可以處於物理上不連續的內存空間中,只要邏輯上連續即可。

如果堆中沒有內存完成實例分配,並且堆也無法再擴展時,將會拋出OutOfMemoryError異常。

線程共享區之方法區

用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

所謂的方法區爲永久代(Permanent Generation)的說法,僅僅是因爲HotSpot虛擬機將GC分代收集擴展至方法區,或者說使用永久代來實現方法區而已。對於其他虛擬機是不存在永久代的說法的。

  6.1  運行時常量池(Runtime Constant Pool)

運行時常量池是方法區的一部分,用於存放編譯器生成的各種字面量和符號引用,一般還會存放翻譯出來的直接引用。這部分內容將在類加載後進入方法區的運行時常量池中存放。

當常量池無法再申請到內存時拋出OutOfMemoryError異常。

 6.2  案例

public class TestJConsole {

    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = new String("abc");
        System.out.println(str1 == str2);
        System.out.println(str1 == str2.intern());

    }

}

結果:

  • 分析如下圖,String str1 = "abc" str1指向常量池,而String str2 = new String("abc"); str2指向堆內存對象,二者地址不同所以str1 == str2結果爲false,但是str2.intern()會把字符串值從堆內存移動到常量池中(如果常量池存在則返回該值的地址),這樣一來str2和str1都是指向常量池的abc。

直接內存

直接內存並不是虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域,但是也會導致OutOfMemoryError異常出現。

例如在JDK1.4中新加入的NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,他可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作。這樣能在一些場景中顯著提高性能,因爲避免了在Java堆和Native堆中來回複製數據。

本機直接內存不會受到Java堆大小的限制,但是會受到本機總內存(包括RAM和SWAP區或者分頁文件)大小以及處理器尋址空間的限制。動態擴展時出現OutOfMemoryError異常。

 

 

 

 

 

 

 

 

 

 

 

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