java 虚拟机的原理_java虚拟机的原理
所謂虛擬機,就是一臺虛擬的機器。它是一款軟件,用來執(zhí)行一系列虛擬計算機指令,大體上虛擬機可以分為系統(tǒng)虛擬機和程序虛擬機,Visual Box 、Vmare就屬于系統(tǒng)虛擬機。他們完全是對物理計算機的仿真,提供了一個可運行完成操作系統(tǒng)的軟件平臺。程序虛擬機典型代表就是java虛擬機,它專門為執(zhí)行單個計算機程序而設(shè)計,在java 虛擬機中執(zhí)行的指令,我們稱為java字節(jié)碼指令。無論是系統(tǒng)虛擬機還是程序虛擬機,在上面運行的軟件都被限制于虛擬機提供的資源中。java發(fā)展至今,出現(xiàn)過很多虛擬機,最初Sun使用的一款叫Classic的Java虛擬機,到現(xiàn)在引用最廣泛的是HotSpot虛擬機,除了Sun以外,還有BEA的JRockit,目前JRockit和HotSpot都被Oracle收入旗下,有整合的趨勢。
Java虛擬機的基本結(jié)構(gòu)
1 類加載子系統(tǒng) : 負責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載Clsaa信息,加載的信息存放在一塊稱之為方法區(qū)的內(nèi)存空間。
2方法區(qū):就是存放類信息、常量信息、常量池信息、包括字符串字面量和數(shù)字常量等。
3java堆:在java虛擬機啟動的時候建立java堆,他是java程序最主要的內(nèi)存工作區(qū)域,幾乎所有的對象實例都存放到j(luò)ava堆中,堆空間是所有線程共享的。
4直接內(nèi)存:Java的NIO庫允許java程序使用直接內(nèi)存,從而提高性能,通常直接內(nèi)存速度會優(yōu)于java堆。讀寫頻繁的場合可能會考慮使用。
5java棧:每個虛擬機線程都有一個私有的棧,一個線程的java棧在線程創(chuàng)建的時候被創(chuàng)建,java棧中保存著局部變量,方法參數(shù)、同時java的方法調(diào)用、返回值等。
6本地方法棧:本地方法棧和java棧非常類似,最大不同為本地方法棧用于本地方法調(diào)用。java虛擬機允許java直接調(diào)用本地方法(通常使用C編寫)。
7垃圾收集系統(tǒng)是java的核心,也是必不可少的,java有一套自己進行垃圾清理的機制,具體的待會說明。
8PC 寄存器也是每個線程私有的空間,java虛擬機會為每個線程創(chuàng)建PC寄存器,在任意時刻,一個java線程總是在執(zhí)行一個方法,這個方法被稱為當(dāng)前方法,如果當(dāng)前方法不是本地方法,PC寄存器就會執(zhí)行當(dāng)前正在被執(zhí)行的命令,如果是本地方法,則PC寄存器值為undefined,寄存器存放如當(dāng)前執(zhí)行環(huán)境指針,程序計數(shù)器,操作棧指針,計算的變量指針等信息。
9虛擬機最核心的組建就是執(zhí)行引擎了,它負責(zé)執(zhí)行虛擬機的字節(jié)碼。一般用戶先進行編譯稱機器碼后執(zhí)行。
堆、棧、方法區(qū)概念和聯(lián)系
堆解決的是數(shù)據(jù)存儲的問題,即數(shù)據(jù)怎么放,放在哪兒。
棧解決程序的運行問題,即程序如何執(zhí)行,或者說如何處理數(shù)據(jù)。
方法區(qū)則是輔助堆棧的快永久區(qū)(Perm),解決堆棧細心的產(chǎn)生,是先決條件。
我們創(chuàng)建一個新的對象,User:那么User類的一些信息(類信息,靜態(tài)信息都存在于方法區(qū)中)
而User類被實例化出來之后,被存儲到j(luò)ava堆中,一塊內(nèi)存空間
當(dāng)我們?nèi)ナ褂玫臅r候,都是使用User對象的引用,形如User user = new User();
辨清java堆
java堆是和java應(yīng)用程序關(guān)系最密切的內(nèi)存空間,幾乎所有的對象都存放其中,并且java堆完全是自動化管理的,通過垃圾回收機制,垃圾對象會自動清理,不需要顯示地釋放。
根據(jù)垃圾回收機制不同,Java堆有可能有不同的結(jié)構(gòu)。最常見的就是將整個java堆分為新生代和老年代。其中新生代存放新生的對象或者年齡不大的對象,老年代則存放老年對象。
新生代分為eden區(qū)、s0區(qū)、s1區(qū),s0和s1也被稱為from和to區(qū)域,他們是兩塊大小相等并且可以互換角色的空間。
絕大多數(shù)情況下,對象首先分配在eden區(qū),在一次新生代回收后,如果對象還存活,則會進入s0和s1區(qū),之后每經(jīng)過一次新生代回收,如果對象存活則它的年齡就加1,當(dāng)對象達到一定年齡后,則進入老年代。
垃圾收集算法
引用計數(shù)法:這是個比較古老而經(jīng)典的垃圾收集算法,其核心就是在對象被其他所引用時計數(shù)器加1,而當(dāng)引用失效時則減1,但是這種方式有非常嚴重的問題:無法處理循環(huán)引用的情況、還有就是每次進行加減操作比較浪費系統(tǒng)性能。
標(biāo)記清除法;就是分為標(biāo)記和清楚兩個階段進行處理內(nèi)存中的對象,當(dāng)然這種方式也有非常大的弊端,就是空間碎片問題,垃圾回收后的空間不是連續(xù)的,不連續(xù)的內(nèi)存空間的工作效率要低于連續(xù)的內(nèi)存空間。
復(fù)制算法:其核心思想就是將內(nèi)存空間分為兩塊,每次只使用其中一塊,在垃圾回收時,將正在使用的內(nèi)存中的存留對象復(fù)制到未被使用的內(nèi)存塊中去,之后去清楚之前正在使用的內(nèi)存塊中所有的對象,反復(fù)去交換兩個內(nèi)存的角色,完成垃圾收集。(java中新生代的from和to空間就是使用這個算法)
標(biāo)記壓縮法:標(biāo)記壓縮法在標(biāo)記清除法基礎(chǔ)之上做了優(yōu)化,把存活的對象壓縮到內(nèi)存一端,而后進行垃圾清理。(java中老年代使用的就是標(biāo)記壓縮法)/
java棧
java棧是一塊線程私有的內(nèi)存空間,一個棧,一般由三部分組成:局部變量表,操作數(shù)棧和幀數(shù)據(jù)區(qū)。
局部變量表:用于報錯函數(shù)的參數(shù)及局部變量:
操作數(shù)棧:主要保存計算過程的中間結(jié)果,同時作為計算過程中變量臨時的存儲空間;
幀數(shù)據(jù)區(qū):除了局部變量表和操作數(shù)棧以外,棧還需要一些數(shù)據(jù)來支持常量池的解析,這里幀數(shù)據(jù)保存著訪問常量池的指針,方便程序訪問常量池,另外,當(dāng)函數(shù)返回或者出現(xiàn)異常時,虛擬機必須由一個異常處理表,方便發(fā)送異常的時候找到異常的代碼,因此異常處理表也是幀數(shù)據(jù)區(qū)的一部分。
java方法區(qū)
java方法區(qū)和堆一樣,方法區(qū)是一塊所有線程共享的內(nèi)存區(qū)域,它保存系統(tǒng)的類信息,比如類的字段,方法,常量池等。方法區(qū)的大小決定了系統(tǒng)可以保存多少個類,如果系統(tǒng)定義太多的類,導(dǎo)致方法區(qū)溢出。虛擬機同樣會拋出內(nèi)存移除錯誤。方法區(qū)可以理解為永久區(qū)(Perm)。
虛擬機參數(shù)
在虛擬機運行的過程中,如果可以跟蹤系統(tǒng)的運行狀態(tài),那么對于問題的故障排查會有一定的幫助,為此,虛擬機提供了一些跟蹤系統(tǒng)狀態(tài)的參數(shù),使用給定的參數(shù)執(zhí)行java虛擬機,就可以在系統(tǒng)運行時打印相關(guān)日志,用于分析實際問題。我們進行虛擬機參數(shù)配置,其實主要就是圍繞著堆,棧,方法區(qū)進行配置。
堆分配參數(shù)
-XX:+PrintGC 使用這個參數(shù),虛擬機啟動后,只要遇到GC就會打印日志。
-XX:+UseSerialGC 配置串行回收器
-XX:+PrintGCDetails 可以查看詳細信息,包括各個區(qū)的情況。
-Xms: 設(shè)置java程序啟動時初始堆大小
-Xmx: 設(shè)置java程序能獲得的最大堆大小
-Xmx20m -Xms5m -XX:+PrintCommandLineFlags: 可以將隱式或者顯示傳給虛擬機的參數(shù)輸出
在實際工作中,可以直接將初始的堆大小和最大堆大小設(shè)置相等,這樣的好處是可以減少程序運行時的垃圾回收次數(shù),從而提高性能。
下面看一個demo:
使用jdk自帶的工具類,打印運行時的內(nèi)存相關(guān)的內(nèi)容,
先看一下沒有配置jvm的打印結(jié)果:
內(nèi)存什么的都很大,看不出具體的內(nèi)存使用情況,下面在看一下使用jvm配置參數(shù)后的打印結(jié)果,
先看一下怎么設(shè)置jvm參數(shù),
設(shè)置玩運行時的JVM,在來看一下打印的結(jié)果:
可以看到j(luò)ava堆的初始內(nèi)存時5M,最大內(nèi)存時20M,第一次分配1M時,使用一開始的初始內(nèi)存,后來,需要再次分配4M的時候,java虛擬器又重新申請了內(nèi)存。可以從total_memory看出來。
Heap里面可以看的到新生代,老年代,永久區(qū)的空間大小和臨界值。
實際項目中,會把jvm的運行環(huán)境放在項目運行的容器中。
新生代的堆參數(shù)配置
-Xmn:可以設(shè)置新生代的大小,設(shè)置一個比較大的新生代會減少老年代的大小,這個參數(shù)對系統(tǒng)性能以及GC行為由很大的影響,新生代大小一般會設(shè)置整個堆空間的1/3到1/4左右。
-XX:SurvivorRatio:用來設(shè)置新生代中eden空間和from/to空間的比例。含義:-XX:SurvivorRatio=eden/from=eden/to
不同的堆分布情況,對系統(tǒng)執(zhí)行會產(chǎn)生一定的影響,在實際工作中,應(yīng)該根據(jù)系統(tǒng)的特點做出合理的配置,基本策略:盡可能將對象預(yù)留在新生代,減少老年代的GC次數(shù)。
除了可以設(shè)置新生代的絕對大小(-Xmn),還可以使用(-XX:NewRatio)設(shè)置新生代和老年代的比例:-XX:NewRatio=老年代/新生代
這個同樣看一個demo:
將3次配置,分別進行jvm配置,然后看一下控制臺的信息,能看到前兩次eden 和 s0或s1都是2:1的內(nèi)存空間,
第三次,老年代:新生代也是2:1的內(nèi)存空間。通過這個配置,可以將JVM的的配置進行細化。
堆溢出處理
在java程序的運行過程中,如果堆空間不足,則會拋出內(nèi)存溢出的錯誤(Out Of Menory) OOM,一旦這類問題發(fā)生在生產(chǎn)環(huán)境,可能引起嚴重的業(yè)務(wù)中斷,java虛擬機提供了-XX:+HeapDumpOnOutOfMemoryError,使用該參數(shù)可以在內(nèi)存溢出時導(dǎo)出整個堆消息,與之配合使用的還有參數(shù),-XX:HeapDumpPath,可以設(shè)置導(dǎo)出堆的存放路徑。
內(nèi)存分析工具:Memory Analyzer 1.5.0
棧配置
Java虛擬機提供了參數(shù)-Xss來指定線程的最大棧空間,整個參數(shù)也直接決定了函數(shù)可調(diào)用的最大深度。
方法區(qū)
和Java堆一樣,方法區(qū)是一塊所有線程共享的內(nèi)存區(qū)域,它用于保存系統(tǒng)的類信息,方案區(qū)(永久區(qū))可以保存多少信息可以對其進行配置,在默認情況下,-XX:MaxPermSize為64MB,如果系統(tǒng)運行時生產(chǎn)大量的類,就需要設(shè)置一個相對合適的方法區(qū),以免出現(xiàn)永久區(qū)內(nèi)存溢出的問題。
-XX:PermSize=64M -XX:MaxPermSize=64M
直接內(nèi)存配置
直接內(nèi)存也是java程序中非常重要的組成部分,特別是廣泛用在NIO中,直接內(nèi)存跳過了java堆,是java程序可以直接訪問原生堆空間,因此在一定程度上加快了內(nèi)存空間的訪問速度。但是說直接內(nèi)存一定就可以提高內(nèi)存訪問速度也不見得,具體情況具體分析。
相關(guān)配置參數(shù):-XX:MaxDirectMemorySize,如果不設(shè)置默認值為最大堆空間,即-Xmx。直接內(nèi)存使用達到上限時,就會觸發(fā)垃圾回收,如果不能有效的釋放空間,也會引起系統(tǒng)的OOM;
在JKD1.7之后,不用考慮這個。
更多詳情可以參照博客
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
總結(jié)
以上是生活随笔為你收集整理的java 虚拟机的原理_java虚拟机的原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中输入char类型_java中如
- 下一篇: linux java查看进程命令_lin