- 虛擬機(jī)大致分為系統(tǒng)虛擬機(jī)(vmware,可運行完整操作系統(tǒng)的軟件平臺)和程序虛擬機(jī)(jvm,專門為執(zhí)行當(dāng)個計算機(jī)程序而設(shè)計),在上面運行的軟件都被限制于虛擬機(jī)提供的資源中
- 虛擬機(jī)的基本結(jié)構(gòu)

1.類加載子系統(tǒng):負(fù)責(zé)從文件系統(tǒng)或網(wǎng)絡(luò)中加載class信息,存放到方法區(qū),也就是一塊內(nèi)存空間
2.*方法區(qū):存放類信息,常量信息,常量池信息,包括字符串字面量和數(shù)字常量等
3.*java堆:jvm啟動的時候建立java堆,java程序最主要的內(nèi)存工作區(qū)域,幾乎所有的對象實例都放在java堆中,堆空間是所有線程共享的,
4.直接內(nèi)存:java的nio庫允許java程序使用直接內(nèi)存,從而提高性能,通常直接內(nèi)存速度會優(yōu)于java堆,讀寫頻繁的場合可能會考慮使用
5.*java棧:每個虛擬機(jī)線程都有一個私有的棧,線程棧在線程創(chuàng)建的時候被創(chuàng)建,線程棧中保存著局部變量,方法參數(shù),java的方法調(diào)用,返回值等等
6.本地方法棧和java棧非常類似 ,但是本地方法棧用于java調(diào)用本地方法(通常使用c編寫)
7.*java 自己的一套進(jìn)行垃圾清理的機(jī)制,稍后詳細(xì)說明
8.pc寄存器,每個線程的私有空間,jvm會為每個線程創(chuàng)建pc寄存器,在任意時刻,一個java線程總是在執(zhí)行一個方法,這個方法稱為當(dāng)前方法,如果當(dāng)前方法不是本地方法,pc寄存器就會執(zhí)行當(dāng)前正在被執(zhí)行的指令,如果是本地方法,pc寄存器值為undefined,pc寄存器存放如當(dāng)前執(zhí)行環(huán)境指針,程序計數(shù)器,操作棧指針,計算的變量指針等
9.*虛擬機(jī)最核心的組件,負(fù)責(zé)執(zhí)行虛擬機(jī)的字節(jié)碼,一般會先進(jìn)行編譯成機(jī)器碼后執(zhí)行
帶*的是重要部分
類加載之后一些信息(類信息,靜態(tài)信息被存放于方法區(qū)中,快永久區(qū)Perm)
類被實例化之后,被存儲到j(luò)ava堆中,一塊內(nèi)存空間
當(dāng)我們使用對象的時候,其實用的是這塊內(nèi)存空間的引用
這個引用存放在java棧中

- java堆詳解
- 幾乎所有的對象都放在其中,java堆是自動化管理的,通過垃圾回收機(jī)制,垃圾對象會自動清理,不需要顯示的釋放
- 根據(jù)垃圾回收機(jī)制的不同,java堆可能會有不同的結(jié)構(gòu),最常見的是將整個java堆分為新生代和老年代,新生代存放新生的對象或者年齡不大的對象,老年代則存放老年對象
- 新生代分為eden區(qū),s0區(qū),s1區(qū),s0和s1區(qū)也被稱為from和to區(qū)域,他們是倆塊大小相等并且可以互換角色的空間
- 絕大多數(shù)情況下,對象首先分配在eden區(qū),在一次新生代回收后,如果對象還存活,則會進(jìn)入s0或者s1區(qū)(或者的原因是在任意時間s0和s1只能有一塊在使用),之后每經(jīng)過一次新生代回收,如果對象存活則它的年齡就加1,當(dāng)對象達(dá)到一定年齡(閾值)后,則進(jìn)入老年代(tenured區(qū),表明在一定考量后,這個數(shù)據(jù)一直被引用,老年區(qū)的數(shù)據(jù)不會被頻繁的回收,新生代則會被頻繁回收)
- s0和s1區(qū)存在的意義在于,這倆塊區(qū)域的垃圾回收使用的是復(fù)制算法,核心思想是將內(nèi)存空間分為倆塊,每次只使用其中一塊,垃圾回收時,將正在使用的內(nèi)存中的存留對象復(fù)制到未被使用的內(nèi)存塊中去,之后去清除之前正在使用的內(nèi)存塊中的所有對象,反復(fù)去交換倆個內(nèi)存的角色,完成垃圾收集。
- java 棧
- 線程私有的內(nèi)存空間,一個棧一般有三部分組成:局部變量表,操作數(shù)棧和幀數(shù)據(jù)區(qū)
- 局部變量表:用于保存函數(shù)的參數(shù)及局部變量
- 操作數(shù)棧:主要保存計算過程的中間結(jié)果,同時作為計算過程中變量臨時的存儲空間
- 幀數(shù)據(jù)區(qū):除了局部變量表和操作數(shù)棧之外,棧還需要一些數(shù)據(jù)來支持常量池的解析,幀數(shù)據(jù)區(qū)保存著常量池的指針,方便程序訪問常量池,此外,當(dāng)函數(shù)返回或者出現(xiàn)異常時,虛擬機(jī)必須有一個異常處理表,方便發(fā)送異常的時候找到異常的代碼,因此異常處理表也是幀數(shù)據(jù)區(qū)的一部分。
- java方法區(qū)
- 和堆一樣,方法區(qū)是一塊所有線程共享的內(nèi)存區(qū)域,它保存系統(tǒng)的類信息,比如類的字段,方法,常量池等,方法區(qū)的大小決定了系統(tǒng)可以保存多少個類,如果系統(tǒng)定義太多的類,導(dǎo)致方法區(qū)溢出。虛擬機(jī)同樣會拋出內(nèi)存溢出錯誤。方法區(qū)可以理解為永久區(qū)(Perm)
- 虛擬機(jī)參數(shù)
- 圍繞堆,棧,方法區(qū),垃圾回收機(jī)制 配置
- 堆分配參數(shù):
- -xx:+PrintGC 虛擬機(jī)啟動后,只要遇到GC就會打印日志
- -xx:+UseSerialGC 配置串行回收器
- -xx:+PrintGCDetails gc時可以查看詳細(xì)信息,包括各個區(qū)的情況
- -Xms java程序啟動時初始堆大小
- -Xmx java程序能獲得的最大堆大小
- -Xmn 設(shè)置新生代大小,這個參數(shù)對gc有較大影響,一般設(shè)置為整個堆大小的三分之一到四分之一左右
- -XX:SurvivorRatio 設(shè)置新生代中eden空間和from或to空間的比例,如果等于3,說明eden區(qū)比上from或to區(qū)為3:1
- -XX:NewRatio 設(shè)置新生代和老年代的比例
- -XX:+PrintCommandLineFlags 可以將虛擬機(jī)的參數(shù)輸出
- 總結(jié),實際工作中可以將初始堆大小和最大堆大小設(shè)置相等,這樣可以減少程序運行時的垃圾回收次數(shù),從而提高性能
-
GCdetails解讀
GCdetails- def new generation 新生代,分為eden區(qū),from區(qū),to區(qū),后面幾列是總空間,使用空間百分比,最后是內(nèi)存地址
- tenured generation 老年代,后面幾列是總空間,使用空間百分比,最后是內(nèi)存地址
- compacting perm gen 永久區(qū)(perm)
- 堆溢出處理
- 如果堆空間不足,則會拋出內(nèi)存溢出的錯誤(Out Of Memory,OOM),這類問題如果發(fā)生在生產(chǎn)環(huán)境,可能會引起嚴(yán)重的業(yè)務(wù)中斷.
- -XX:HeapDumpOnOutOfMemoryError,使用該參數(shù)可以再內(nèi)存溢出時導(dǎo)出整個堆信息
- -XX:HeapDumpPath 設(shè)置導(dǎo)出堆信息的存放路徑,與上面的參數(shù)配合使用
- 例子:
-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Test03.dump - dump文件分析工具
- 分析方法
- 代碼示例
public static void main(String[] args) { //-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Test03.dump //堆內(nèi)存溢出 Vector v = new Vector(); for(int i=0; i < 5; i ++){ v.add(new Byte[1*1024*1024]); } } - 棧參數(shù)配置
- -Xss 指定單個線程的最大??臻g,??臻g的大小直接決定了函數(shù)可調(diào)用的最大深度
- 代碼示例
public class Test04 { //-Xss1m //-Xss5m //棧調(diào)用深度 private static int count; public static void recursion(){ count++; recursion(); } public static void main(String[] args){ try { recursion(); } catch (Throwable t) { System.out.println("調(diào)用最大深入:" + count); t.printStackTrace(); } } } - 方法區(qū)參數(shù)配置
- 方法區(qū)保存系統(tǒng)的類信息
- 默認(rèn)情況下,-XX:MaxPermSize 為64MB,如果系統(tǒng)運行時產(chǎn)生大量的類,就需要設(shè)置一個相對合適的方法區(qū),以免永久區(qū)內(nèi)存溢出的問題
- -XX:PermSize 初始化方法區(qū)大小
- -XX:MaxPermSize 方法區(qū)最大大小
- 直接內(nèi)存參數(shù)配置
- -XX:MaxDirectMemorySize,如果不設(shè)置,默認(rèn)值為最大堆空間,即-Xmx,直接內(nèi)存使用達(dá)到上限時,會觸發(fā)垃圾回收,如果不能有效的釋放空間,也會引起系統(tǒng)的OOM,
- jdk1.7之后,不再關(guān)注這個參數(shù),配不配都沒什么大影響
- Client和Server虛擬機(jī)工作模式
- 只有在jdk1.7之前,jdk才分這倆個模式,1.7之后不再提供client模式,使用-client參數(shù)使用Client模式,使用-server使用Server模式,
- 區(qū)別
- Client模式啟動較快,適用于測試,不追求系統(tǒng)的長時間使用性能
- Server模式啟動較慢,會對虛擬機(jī)的性能進(jìn)行負(fù)載的系統(tǒng)性能信息收集和使用更復(fù)雜的算法對程序進(jìn)行優(yōu)化,長期運行性能遠(yuǎn)遠(yuǎn)快于client模式
- 關(guān)于jvm的博客參考
