java 內存區域

學習《深入理解Java 虛擬機》做些總結。並有參考其他博客的地方。如有不規範大家指出相互學習提高。我們知道java 虛擬機在執行java 程序的過程中會把它所管理的內存劃分爲若干不同的數據區域,這些區域都有各自的用途,以及創建和銷燬的時間。有的區域隨着虛擬機進程(方法區、堆)的啓動而存在,有些區域則依賴線程的啓動和結束而建立和銷燬(程序計數器、虛擬機棧、本地方法棧),先看下運行是數據區如下圖:

這裏寫圖片描述

此圖只是概念意義,並非實際分配內存大小比例。

虛擬機棧

虛擬機棧描述的是Java 方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每個方法從調用直至執行完成的過程,就對應這一個棧幀在虛擬機棧中入棧到出棧的過程
看如下簡圖

這裏寫圖片描述

我們常說的java 內存空間分爲堆內存(Heap)和棧內存(Stack),這種分法比較粗糙,只是我們平時最關注的、與對象內存分配關係最密切的內存區域是這兩塊,堆 下面會提到,棧內存就是這裏的虛擬機棧。

棧幀的結構圖:

這裏寫圖片描述

局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用和returnAddress類型

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

  • 異常例子

    在設置棧內存後進行代碼測試,此例子中設置爲128 K

package com.xnccs.cn.share;


/**
 * 棧溢出測試
 * 
 * VM : -Xss128k  棧內存容量
 * @author j_nan
 *
 */
public class JavaVMStackSOF {

    private int stackLength = 1;


    public void stackLeak(){
        stackLength ++;
        stackLeak();
    }


    public static void main(String[] args) {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try{
            oom.stackLeak();
        }catch(Throwable e){
            System.out.println("stack length:" + oom.stackLength);
            throw e;
        }
    }
}

輸出:
這裏寫圖片描述

  • 代碼優化點
    由上我們已經知道局部變量表所需的內存空間在編譯期完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的局部變量空間是完全確定的,在方法的運行期間不會改變局部變量表的大小。
    所以當方法內的參數過多時,我們可以採取使用參數類來代替也減小局部變量表的大小使得相同棧大小的情況下有更高的棧深。以上個例子來說,如果我們將stackLeak()方法改成 十多個參數,那絕對不會等到stack lenght:991 的時候才報異常。本人試過在stack length:434 時就報錯,大家可以自行試試就不再貼代碼了

本地方法棧

本地方法棧與虛擬機棧所發揮的作用是非常相似的,它們之前的區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則爲虛擬機使用到的native 方法服務。

如 System.currentTimeMillis(); public static native long currentTimeMillis(); 此獲取當前毫秒就是native方法

  • 異常
    與虛擬機棧一樣,本地方法棧區域也回會拋出StackOverflowError 和 OutOfMemoryError異常

方法區

方法區與java堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼數據等數據

類信息,每個類的全限定名、每個類的直接超類的全限定名、該類的類還是接口、該類型的訪問修飾符,直接超接口的全限定名的有序列表

即時編譯器編譯後的代碼, jvm 加載執行的是二進制字節碼Class文件,它最終會翻譯成系統能識別機器碼,這個過程最初是通過解釋器進行解釋執行的,當虛擬機發現某個方法或代碼塊的運行特別頻繁時,就會把這些代碼認定爲“熱點代碼”。爲了提高熱點代碼的執行效率,在運行時虛擬機將會把這些代碼編譯成與本地平臺相關的機器碼,並進行各種層次的優化,以便下次直接使用這些編譯後的機器碼來提高運行效率

  • 異常
    當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常

對於大多數應用來說,Java 堆 是Java 虛擬機所管理的的內存中最大的一塊,此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。在Java 虛擬機規範中的描述是:所有的對象以及數組都要在堆上分配,但是隨着JIT編譯器的發展與逃逸分析技術逐漸成熟,棧上分配、標量替換優化技術將會導致一些微妙的變化發生,所有的對象都分配在堆上也就不那麼絕對了。

簡單理解就是,在堆上的大部分對象都會在發生GC 時被回收釋放內存,但GC 同樣是需要耗費資源,那如果確定一個對象只在一個方法區內出現,不存在逃逸現象,則可以考慮將此對象創建在棧上,隨着方法的執行完成而回收對象的空間也就優化了GC的負擔。但就我們常見的HotSpot 虛擬機來說目前沒有這種機制,不存在在棧上創建對象的情況。

Java 堆是垃圾收集器管理的主要區域,從內存回收的角度來看,由於現在收集器基本都採用分代收集算法,所以Java堆中還可以細分爲:新生代和老年代;新生代中還可以分爲Eden空間、From Survivor空間、To Survivor空間。它們比例默認是 8:1:1 。

這裏寫圖片描述

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

  • 異常例子

package com.xnccs.cn.share;

import java.util.ArrayList;
import java.util.List;

/**
 * 堆溢出測試
 * 
 * VM: -Xms20m -Xmx20m
 * @author j_nan
 *
 */
public class HeapOOM {

    static class OOMObject{
    }

    public static void main(String[] args) throws InterruptedException {
        List<OOMObject> list = new ArrayList<OOMObject>();
        while(true){
            try{
                list.add(new OOMObject());
            }catch(Throwable e){
                System.out.println("集合中對象數量是:"+list.size());
                throw e;
            }
        }

    }
}

程序計數器

程序計數器,指向當前線程正在執行的字節碼指令地址。詳細內容已經總結過,地址如下:
http://blog.csdn.net/leaf_0303/article/details/78953669

java 內存情況,在其他博客中看到此圖,感覺挺好理解

這裏寫圖片描述

直接內存

直接內存,已經單列章節,地址如下:
http://blog.csdn.net/leaf_0303/article/details/78961936

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