[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引數:
這裡-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)
相關推薦
[jjzhu學java]深入理解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
深入理解JVM之JVM內存區域與內存分配
錯誤 銷毀 構造方法 初學 不存在 data 空閑 table fin 深入理解JVM之JVM內存區域與內存分配 在學習jvm的內存分配的時候,看到的這篇博客,該博客對jvm的內存分配總結的很好,同時也利用jvm的內存模型解釋了java程序中有關參數傳遞的問題。