1. 程式人生 > >[jjzhu學java]深入理解JVM筆記之記憶體管理機制

[jjzhu學java]深入理解JVM筆記之記憶體管理機制

深入理解JVM筆記之記憶體管理機制

執行時資料區域

程式計數器

每個執行緒都有一個程式計數器(PC),是當前執行緒所執行的位元組碼的行號指示器,通過改變程式計數器的值來選取下一條指令。各執行緒之間的計數器互不影響,是執行緒私有的記憶體。
如果執行緒執行的是一個JAVA方法,則計數器記錄的為正在執行的位元組碼指令的地址,如果執行的是Natvie方法,這計數器的值為空(Undifined)。
程式計算器所在的記憶體區域是唯一一個JVM規範中沒有規定OOMError的記憶體區域

JVM棧

JVM棧也是每個執行緒所私有的記憶體區域,隨著執行緒的建立而分配,執行緒的消亡而回收。JVM棧是描述Java方法執行的記憶體模型,每個java方法在執行的時候,都會建立一個棧幀(Stack Frame),其作用是用來儲存java方法中的區域性變數、操作棧、方法出口等資訊。每一個java方法從被呼叫到執行完畢的過程,都伴隨著對應的棧幀在JVM棧中的進棧和入棧。
JVM規範中,該區域中有兩個異常狀況:
-StackOverflowError:執行緒請求的棧深度超過了JVM棧所允許的棧深度


-OutOfMemoryError:如果JVM棧是動態可擴充套件的,在無法申請到足夠記憶體時,丟擲該異常

本地方法棧

本地方棧(Native Method Stack)是描述虛擬機器所用到的本地方法的記憶體模型,與JVM棧類似,也會丟擲StackOverflowError和OutOfMemoryError

Java堆

Java堆是java虛擬機器所管理的最大的記憶體區域,我們所說的垃圾回收,就是對該區域的記憶體進行回收,所以也叫GC堆。java堆是所有執行緒所公用的記憶體區域,程式中的物件、陣列都存放在該區域中。
從記憶體回收的角度來看,由於現在的收集器所採用的都是分代收集演算法,所以java堆可以在進一步細分為:新生代和老年代,在新生代中,又可以劃分為Eden、From survivor、To survivor等空間。從記憶體分配的角度來看,java堆可能劃分為多個執行緒私有的分配快取區。
java堆可以是物理上不連續但邏輯上要連續的記憶體空間,可以通過指定-Xmx(最大)、-Xms(最小)、-Xmn(新生代)等VM引數來設定java堆得大小。若有新物件申請記憶體空間但是java堆沒有足夠的記憶體分配時,會報OOMError。

方法區

方法區(Method Area)也是各執行緒共享的記憶體區域,用於儲存被虛擬機器載入的類資訊、常量、靜態常量、編譯後的程式碼等資料看,其還有一個別名叫Non-Heap(非堆),與java堆加以區分。在HotSpot虛擬機器上,也可叫做“永久代”(Permanent Generation),本質上不等價(HotSpot將GC收集擴充套件到了該區域)。
若要對該區域進行GC,主要是回收常量池以及對型別的解除安裝。該區域在記憶體滿後也會丟擲OOMError異常。

執行時常量池

執行時常量池(Runtime Constant Pool)是方法區內的一部分,Class檔案中除了類的版本、欄位、方法、介面等描述資訊,還常量池表,用於存放編譯期間生成的葛總常量和符號引,這部分內容在類載入後存放執行時常量池這種(class如何載入會在後面章節提及)。執行時常量池具備動態性,java語言不單單在編譯期間會產生常量,在執行期間也可能將新的常量放入執行時常量池中,如開發人員用了String.intern()方法,之後的異常測試就是用該方法引起常量池丟擲OOMError。

直接記憶體

直接記憶體(Direct Memory)不是虛擬機器執行時資料區域的一部分,也不是JVM規範中定義的記憶體區域,但其也頻繁被使用。自JDK1.4後,引入了基於通道(Channel)與緩衝區的I/O方式,它可以使用native函式庫直接分配堆外記憶體,並通過java堆中的DirectByteBuffer物件操作該塊記憶體。避免了java堆和native堆之間的資料複製,提高了效能。
本機直接記憶體不受java堆大小限制,但是受本機總記憶體大小和處理器的定址空間限制。在動態擴充套件時也會丟擲OOMError異常。

物件訪問

OutOfMemoryError異常

之前對虛擬機器執行時資料區域進行了描述,現在通過例項來驗證OOM異常發生的情況,可以進一步瞭解jvm各記憶體區域的儲存內容,及發生異常的情況。
測試的java版本為:

java version “1.7.0_80”
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

Java堆溢位示例

之前已經提到,物件例項都儲存在java堆中,所以可以不斷的建立物件且保證物件不被GC就可以讓java堆溢位
編寫如下程式碼清單:

/** 
* @ClassName: HeapOOM 
* @Description: Java堆記憶體溢位異常測試
* @author 祝佳俊([email protected])
* @date 2016年10月18日 下午8:30:13 
* VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
    static class OOMObject{ 
    }

    public static void main(String[] args) {
        List<OOMObject> list = new  ArrayList<OOMObject>();
        while(true){
            list.add(new OOMObject());
        }
    }
}

並配置執行時的VM引數:
VM引數配置
這裡-XX:UseParNewGC是指定用ParNew收集器,預設的是
引數解釋: -Xms:20M:java堆最小20M
-Xmx:20M:java堆最大20M(避免動態擴充套件)
-XX:+HeapDumpOnOutOfMemoryError:開啟該選項,可以讓虛擬機器在丟擲OutOfMemoryError時Dump出當前記憶體堆轉存快照
執行後,就會有如下輸出:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3576.hprof ...
Heap dump file created [34233497 bytes in 0.301 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2760)
    at java.util.Arrays.copyOf(Arrays.java:2734)
    at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
    at java.util.ArrayList.add(ArrayList.java:351)
    at study.hard.jjzhu.HeapOOM.main(HeapOOM.java:20)

JVM棧和本地方法棧溢位

之前有提過,HotSpot虛擬機器並沒有區分JVM和本地方法棧。在HotSpot虛擬機器中可以通過制定-Xss引數來指定棧的大小。
編寫如下測試程式碼:

/** 
 * @ClassName: JavaVMStackSOF 
 * @Description: 虛擬機器棧和本地方法棧OOM測試 
 * @author 祝佳俊([email protected])
 * @date 2016年10月18日 下午9:09:21 
 *  VM Args: -Xss128k
 */
public class JavaVMStackSOF {
    private int stackLength = 1;
    public void stackLeak(){
        stackLength ++;
        //遞迴呼叫
        stackLeak();

    }

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

執行後就會報StackOverflowError異常:

stack deep:1007
Exception in thread "main" java.lang.StackOverflowError
    at study.hard.jjzhu.memory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
    at study.hard.jjzhu.memory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:15)
    at study.hard.jjzhu.memory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:15)
    at study.hard.jjzhu.memory.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:15)

執行時常量池溢位

String.intern()方法可以向執行時常量池中新增內,所以要達到執行時常量池溢位,執行用本地方法intern()像池中不斷新增字串即可,可以同過-XX:PermSize -XX:MaxPermSize限制方法區大小。

/** 
 * @ClassName: RuntimeConstantPoolOOM 
 * @Description: 方法區和執行時常量池溢位
 * @author 祝佳俊([email protected])
 * @date 2016年10月18日 下午9:18:07 
 * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
 */
public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        int i = 0 ;
        while(true){
            /* 
             * String.intern()是一個Native方法,作用是:如果字串常量池中已經包含一個等價於此String
             * 物件的字串,則返回常量池中這個字串的String物件,否則,將此String物件包含的字串新增到常量池中,
             * 並返回此String物件的引用
            */
            list.add(String.valueOf(i++).intern());
        }
    }
}
>執行結果:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
    at java.lang.String.intern(Native Method)
    at study.hard.jjzhu.memory.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:23)

本機直接記憶體溢位

本機直接記憶體可以通過 -XX:MaxDirectMemorySize指定記憶體大小,若不指定,則預設與java堆得最大值一樣。

/** 
* @ClassName: DirectMemoryOOM 
* @Description:  本機直接記憶體溢位
* @author 祝佳俊([email protected])
* @date 2016年10月18日 下午9:41:40 
* VM Args: -Xmx20M -XX:MaxDirectMemorySize=20M
*/
public class DirectMemoryOOM {
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe)unsafeField.get(null);
        while(true){
            unsafe.allocateMemory(_1MB);
        }
    }
}
Exception in thread "main" java.lang.OutOfMemoryError
    at sun.misc.Unsafe.allocateMemory(Native Method)
    at study.hard.jjzhu.memory.DirectMemoryOOM.main(DirectMemoryOOM.java:21)

相關推薦

[jjzhujava]深入理解JVM筆記記憶體管理機制

深入理解JVM筆記之記憶體管理機制 執行時資料區域 程式計數器 每個執行緒都有一個程式計數器(PC),是當前執行緒所執行的位元組碼的行號指示器,通過改變程式計數器的值來選取下一條指令。各執行緒之間的計數器互不影響,是執行緒私有的記憶體。

深入理解JVM筆記記憶體管理機制

執行時資料區域 程式計數器 每個執行緒都有一個程式計數器(PC),是當前執行緒所執行的位元組碼的行號指示器,通過改變程式計數器的值來選取下一條指令。各執行緒之間的計數器互不影響,是執行緒私有的記憶體。  如果執行緒執行的是一個JAVA方法,則計數器記錄的為正在執行的位

深入理解JVM(二)自動記憶體管理機制

2.1 C、C++記憶體管理是由開發人員管理,而Java則交給了JVM進行自動管理 2.2 JVM執行時資料區:方法區、堆(執行時執行緒共享),虛擬機器棧、本地方法棧、程式計數器(執行時執行緒隔離,私有)   2.2.1 程式計數器(Program Counter Register):每一個執行緒都獨有一

CSS深入理解學習筆記padding

style left 使用 支持 背景色 cnblogs check chrom 所有 1、padding與容器尺寸之間的關系   對於block水平元素:①padding值暴走,一定會影響尺寸;②width非auto,padding影響尺寸;③width為auto或box

CSS深入理解學習筆記relative

需要 定位 層疊 left 避免 str 作用 屬性 存在 1、relative和absolute的相煎關系   限制作用:①限制left/top/right/bottom定位;②限制z-index層級;③限制在overflow下的囂張氣焰。   relative和fixe

CSS深入理解學習筆記line-height

初始 -a 上下 normal 每一個 不同 單行 盒子模型 等於 1、line-height的定義   定義:兩行文字基線之間的距離。   註:不同字體之間的基線是不同的。 2、line-height與行內框盒子模型   行內框盒子模型:   ①內容區域(content

Java併發程式設計實踐筆記——加鎖機制(Locking)

多個狀態變數的一致性保持 當執行緒安全的不變性條件中涉及多個變數時,並且各個變數之間不是彼此獨立,某個變數對其他變數的值產生約束。那當更新一個變數時,要在同一個原子操作中更新其他的變數內建鎖(Intrinsic Locks) Java提供了同步程式碼塊(Synchroni

深入理解JVM】:類載入機制

概述 虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制。 與那些在編譯時需要進行連結工作的語言不同,在Java語言裡,型別的載入、連線和初始化過程

深入理解作業系統原理程序管理(一)

一、概述 1、為什麼引入程序 程式併發執行時具有如下特徵: 間斷性 程式在併發執行時,由於它們共享資源或為完成同一項任務而相互合作,使在併發程式之間形成了相互制約的關係。相互制約將導致併發程式具有“執行-暫停-執行”這種間斷性活動規律。 失去封閉性

深入理解計算機系統--記憶體定址(四)--linux中分段機制的實現方式

linux中的分段機制 前面說了那麼多關於分段機制的實現,其實,Linux以非常有限的方式使用分段。因為,Linux基本不使用分段的機制(注:並不是不使用,使用分段方式還是必須的,會簡化程式的編寫和執行方式),或者說,Linux中的分段機制只是為了相容IA

Objective-C高階程式設計讀書筆記記憶體管理

Objective-C高階程式設計 iOS與OS X多執行緒和記憶體管理 自動引用計數(ARC, Automatic Reference Counting) 目錄 什麼是自動引用計數 記憶體管理的思考方式 autorelease 所有權修飾符介紹 ARC規則 ARC實

從零開始的jvm記憶體管理機制

該篇源自於對《深入理解java虛擬機器》的學習和總結。大牛拍磚請輕點。 1、執行時資料區域 1.1 程式計數器 定義:當前執行緒所執行的位元組碼的行號指示器。 設計目的:為了執行緒切換後能恢復到正確的執行位置,所以需要每個執行緒都擁有一個獨立的程式計數器。 注意: a、

黑馬程式設計師——OC筆記記憶體管理

Objective-C提供了三種記憶體管理方式: MannulReference Counting(MRC,手動管理, iOS4.1之前的版本) automatic reference counting(ARC,自動引用計數,iOS4.1 之後推出的) garbage collection(垃圾回收)。iO

Python記憶體管理機制

Python使用gc模組處理python物件以及python垃圾回收器的工作,完整的gc模組文件參考這裡。舉gc模組的幾個例子看一下: gc.enable()——可自動進行垃圾回收;gc.disable()——不可自動進行垃圾回收;gc.set_threshold()——

Android學習 記憶體管理機制與應用記憶體優化

    Random Access Memory(RAM)在任何軟體開發環境中都是一個很寶貴的資源。這一點在實體記憶體通常很有限的移動作業系統上,顯得尤為突出。儘管Android的Dalvik虛擬機器扮演了常規的垃圾回收的角色,但這並不意味著你可以忽視app的記憶體分配與釋放的時機與地點。於大多數apps來說

【讀書筆記JAVA基礎:1、深入理解JVM

  通過《深入理解JAVA虛擬機器》和《深入理解計算機系統》兩本經典著作的學習,注重瞭解系統程序執行時記憶體結構的變化,以此徹底瞭解JVM虛擬機器在執行JAVA程式時的記憶體結構!       主要有三個方面:   &nb

深入理解JVM學習筆記(五、JAVA發展歷史)

一、JDK Version 1.0 開發代號為Oak(橡樹),於1996-01-23發行       其提出了“Write Once,Run Anywhere”的口號二、JDK Version 1.1於1997-02-19發行。引入的新特性包括:引入JDBC(Java Dat

筆記深入理解JVM 第2章 Java記憶體區域與記憶體溢位

1、JVM 執行時資料區 所有執行緒共享的資料區:方法區(持久代)、堆區 執行緒隔離的資料區:程式計數器、Java虛擬機器棧區 堆區構成:新生代 ( 由Eden, From Survivor, To Survivor 構成)、老生代 執行時常量池:方法區一部分,用於存放編

讀書筆記-深入理解JVM虛擬機-1.OOM初探

-a 最大 xms stat new 理解 虛擬機 class 當前 Java堆OOM(Out-Of-Memory)異常 執行例如以下程序,爆出異常 java.lang.OutOfMemoryError: Java heap space /** * VM Ar

深入理解JVMJVM內存區域與內存分配

錯誤 銷毀 構造方法 初學 不存在 data 空閑 table fin 深入理解JVM之JVM內存區域與內存分配   在學習jvm的內存分配的時候,看到的這篇博客,該博客對jvm的內存分配總結的很好,同時也利用jvm的內存模型解釋了java程序中有關參數傳遞的問題。