From:深入理解Java虛擬機
- 目錄
BiBi - JVM -0- 開篇
BiBi - JVM -1- Java內(nèi)存區(qū)域
BiBi - JVM -2- 對象
BiBi - JVM -3- 垃圾收集算法
BiBi - JVM -4- HotSpot JVM
BiBi - JVM -5- 垃圾回收器
BiBi - JVM -6- 回收策略
BiBi - JVM -7- Java類文件結(jié)構(gòu)
BiBi - JVM -8- 類加載機制
BiBi - JVM -9- 類加載器
BiBi - JVM -10- 虛擬機字節(jié)碼
BiBi - JVM -11- 編譯期優(yōu)化
BiBi - JVM -12- 運行期優(yōu)化
BiBi - JVM -13- 并發(fā)
1. 程序計數(shù)器
占用內(nèi)存空間較小,是唯一一個沒有規(guī)定任何OutOfMemoryError的區(qū)域丽涩。
字節(jié)碼解釋器通過改變這個計數(shù)器的值來選取下一條執(zhí)行的字節(jié)碼指令绪氛,并且分支、循環(huán)、跳轉(zhuǎn)勾拉、異常處理粘姜、線程恢復(fù)等功能都需要依賴計數(shù)器來完成。
每個線程都有一個獨立的計數(shù)器乞榨,來恢復(fù)線程的切換秽之。
執(zhí)行java方法時,計數(shù)器記錄虛擬機字節(jié)碼指令的地址吃既;執(zhí)行native方法時考榨,計數(shù)器值為null。
2. 虛擬機棧
線程私有鹦倚;每個方法執(zhí)行都會創(chuàng)建一個棧楨河质,用于存儲局部變量表、操作數(shù)棧震叙、動態(tài)鏈接掀鹅、方法出口等。
Slot:長度為64位的long和double類型的數(shù)據(jù)會占用2個局部變量空間【Slot】媒楼。
局部變量表所需的內(nèi)存空間在編譯期間完成分配乐尊,當(dāng)一個方法進入時,這個方法在棧楨中分配多大的局部空間是完全確定的划址,在運行期間不會改變局部變量表的大小扔嵌。
異常拋出情況:
1)棧深度過大限府,拋出StackOverflowError。
2)棧動態(tài)擴展時痢缎,無法申請到足夠空間胁勺,拋出OutOfMemoryError。
3. 本地方法棧
與虛擬機棧相似牺弄,拋出異常一樣姻几。區(qū)別:虛擬機棧對應(yīng)java方法【字節(jié)碼】;本地方法棧對應(yīng)native方法【機器碼】势告。
HotSpot虛擬機將虛擬機棧和本地方法棧合二為一蛇捌。
程序計數(shù)器、虛擬機棧咱台、本地方法棧3個區(qū)域隨線程而生络拌,隨線程而滅。
4. 堆
被所有線程共享回溺;所有對象和數(shù)組都在堆上分配春贸,但是隨著JIT編譯器的發(fā)展和逃逸分析技術(shù)的成熟,棧上分配和標(biāo)量替換優(yōu)化技術(shù)遗遵,使得其不再絕對萍恕。
當(dāng)堆無法擴展時,拋出OutOfMemoryError车要。
5. 方法區(qū)
與堆一樣允粤,被所有線程共享。
主要存儲已經(jīng)被虛擬機加載的【類信息翼岁,如:類名类垫、訪問修飾符、字段描述琅坡、方法描述】【常量池】【靜態(tài)變量】【即時編譯器編譯后的代碼】等悉患。
HotSpot虛擬機將方法區(qū)稱為:永久代,本質(zhì)上二者不同榆俺,只是使用永久代來實現(xiàn)方法區(qū)的GC【把GC分代收集擴展到方法區(qū)】售躁,省去了為方法區(qū)專門管理GC的工作。對于其他虛擬機【如:BEA的JRockit茴晋、IBM的J9】不存在“永久代”的概念迂求。這個區(qū)域的內(nèi)存回收目標(biāo)主要是針對【常量池的回收】和【對類型的卸載】,GC的時機很少晃跺。
由于容易引起內(nèi)存泄漏,HotSpot虛擬機計劃放棄“永久代”毫玖,采用Native Memory來實現(xiàn)方法區(qū)的規(guī)劃掀虎。目前JDK1.7中已經(jīng)把原本在“永久代”中的【字符串常量池】移除了凌盯。
6. 運行時常量池
是方法區(qū)的一部分【jdk1.7之前,之后分離開來】烹玉。
主要存放編譯期間生成的各種字面常量和符號引用驰怎。具有動態(tài)性,運行期間也可以將新的常量放入池中二打,如:利用Sting類中的intern()方法县忌,它的作用:如果字符串常量池中已經(jīng)包含一個等于此String對象的字符串,則返回這個字符串對象继效;否則症杏,將次String對象包含的字符串添加到常量池,并返回此String對象的引用瑞信。
- 例子1
String str = new StringBuilder("計算機").append("軟件").toString();
System.out.println(str.intern() == str);
在JDK1.6及以前厉颤,結(jié)果為false。因為:intern()方法會把首次遇到的字符串“計算機軟件”實例復(fù)制到永久代凡简,并返回永久代中這個字符串的實例引用逼友,所有結(jié)果為false。
字JDK1.7中秤涩,結(jié)果為true帜乞。因為:intern()不會再復(fù)制實例,只是在常量池中記錄首次出現(xiàn)的實例引用筐眷,所以結(jié)果為true黎烈。
- 例子2
String s = "java";
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
無論什么JDK版本,結(jié)果都為false浊竟。因為"java"在常量池中已經(jīng)出現(xiàn)過了怨喘,所以str2.intern()返回的是常量池中的引用。
7. 直接內(nèi)存
一種基于通道和緩沖區(qū)的I/O方式振定,它可以使Native數(shù)據(jù)直接分配堆外內(nèi)存必怜,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。這樣能夠提高性能后频,避免Java在堆和Native堆中來回復(fù)制數(shù)據(jù)梳庆。
直接內(nèi)存也稱為C-Heap,供Java Runtime進程使用卑惜,沒有相應(yīng)的參數(shù)來控制其大小膏执,其大小依賴于操作系統(tǒng)進程的最大值。Java應(yīng)用程序都是在Java Runtime Environment (JRE)中運行露久,而Runtime本身就是由Native語言(如:C/C++)編寫程序更米。Native Memory就是操作系統(tǒng)分配給Runtime進程的可用內(nèi)存,它與Java Heap Memory不同毫痕,Java Heap 是Java應(yīng)用程序的內(nèi)存征峦。
- Native Memory的主要作用如下:
- 管理java heap的狀態(tài)數(shù)據(jù)(用于GC)迟几;
- JNI調(diào)用,也就是Native Stack栏笆;
- JIT(即使編譯器)編譯時使用Native Memory类腮,并且JIT的輸入(Java字節(jié)碼)和輸出(可執(zhí)行代碼)也都是保存在Native Memory;
- NIO direct buffer蛉加;
- Threads蚜枢;
- 類加載器和類信息都是保存在Native Memory中的。
- 小常識
- Java Heap是對于Java 虛擬機而說的针饥,一般的大小上限是 16M 24M 48M 76M 具體視手機而定厂抽。
- Native Heap是對于C/C++直接操縱的系統(tǒng)堆內(nèi)存,所以它的上限一般是具體RAM的2/3左右打厘。
- 所以對于手機而言修肠,Java Heap 大概76M,而Native Heap是760M左右户盯,相差10倍嵌施。