java 执行机制_Java类的执行机制
在完成將class文件信息加載到JVM并產(chǎn)生Class對(duì)象后,就可執(zhí)行Class對(duì)象的靜態(tài)方法或?qū)嵗瘜?duì)象進(jìn)行調(diào)用了。在源碼編譯階段將源碼編譯為JVM字節(jié)碼,JVM字節(jié)碼是一種中間代碼的方式,要由JVM在運(yùn)行期對(duì)其進(jìn)行解釋并執(zhí)行,這種方式成為字節(jié)碼解釋執(zhí)行方式
字節(jié)碼解釋執(zhí)行
由于采用的為中間碼的方式,JVM有一套自己的指令,對(duì)于面向?qū)ο蟮恼Z(yǔ)言而言,最重要的是執(zhí)行方法的指令,JVM采用了invokestatic、invokevirtual、invokeinterface、invokespecial四個(gè)指令來(lái)執(zhí)行不同的方法調(diào)用。invokestatic對(duì)應(yīng)的是調(diào)用static方法,invokevirtual對(duì)應(yīng)的是調(diào)用對(duì)象實(shí)例的方法,invokeinterface對(duì)應(yīng)的是調(diào)用接口的方法,invokespecial對(duì)應(yīng)的是調(diào)用private方法和編譯源碼后生成的方法,此方法為對(duì)象實(shí)例化時(shí)的初始化方法(構(gòu)造方法)。
Sun
JDK基于棧的體系結(jié)構(gòu)來(lái)執(zhí)行字節(jié)碼,基于棧方式的好處是代碼緊湊,體積小。線(xiàn)程在創(chuàng)建后,都會(huì)產(chǎn)生程序計(jì)數(shù)器(PC)和棧(Stack):PC存放了下一條要執(zhí)行的指令在方法內(nèi)的偏移量,棧中存放了棧幀,每個(gè)方法每次調(diào)用都會(huì)產(chǎn)生棧幀。棧幀主要分為局部變量區(qū)和操作數(shù)棧兩部分,局部變量區(qū)用于存放方法中的局部變量和參數(shù),操作數(shù)棧中用于存放方法執(zhí)行過(guò)程中產(chǎn)生的中間結(jié)果,棧幀中還會(huì)有一些雜用空間,例如指向方法已解析的常量池的引用、其他一些VM內(nèi)部實(shí)現(xiàn)需要的數(shù)據(jù)等。
1、指令解釋執(zhí)行
對(duì)于方法的指令解釋執(zhí)行,執(zhí)行方式為經(jīng)典馮諾依曼體系中的FDX循環(huán)方法,即獲取下一條指令,解碼并分派,然后執(zhí)行。在實(shí)現(xiàn)FDX循環(huán)式有switch-threading、token-threading、direct-threading、subroutine-threading、inline-threading等多種方式。Sun
JDK的重點(diǎn)為編譯成機(jī)器碼,并沒(méi)有在解釋器上做太復(fù)雜的處理,因此采用了token-threading方式。為了讓解釋執(zhí)行能更加高效,Sun
JDK還做了一些其他的優(yōu)化,主要有:棧頂緩存和部分棧幀共享。
2、棧頂緩存
在方法的執(zhí)行過(guò)程中,可看到有很多操作要將值放入操作數(shù)棧,這導(dǎo)致了寄存器和內(nèi)存要不斷的交換數(shù)據(jù),Sun
JDK采用了一個(gè)棧頂緩存,即將本來(lái)位于操作數(shù)棧的值直接緩存在寄存器上,這對(duì)于大部分只需要一個(gè)值的操作而言,無(wú)需將數(shù)據(jù)放入操作數(shù)棧,可直接在寄存器計(jì)算,然后放回操作數(shù)棧。
3、部分棧幀共享
當(dāng)一個(gè)方法調(diào)用另一個(gè)方法時(shí),通常傳入另一個(gè)方法的參數(shù)為已存放在操作數(shù)棧的數(shù)據(jù),Sun
JDK在此做了一個(gè)優(yōu)化,就是當(dāng)調(diào)用方法時(shí),后一方法可將前一方法的操作數(shù)棧作為當(dāng)前方法的局部變量,從而節(jié)省數(shù)據(jù)copy帶來(lái)的消耗。
編譯執(zhí)行
編譯執(zhí)行的效率較低,為提升代碼的執(zhí)行性能,SunJDK提供將字節(jié)碼編譯為機(jī)器碼的支持,編譯在運(yùn)行時(shí)進(jìn)行,通常稱(chēng)為JIT編譯器。SunJDK在執(zhí)行過(guò)程中對(duì)執(zhí)行頻率高的代碼進(jìn)行編譯,對(duì)執(zhí)行不頻繁的代碼則繼續(xù)采用解釋執(zhí)行的方式,因此SunJDK又稱(chēng)為Hotspot
VM,在編譯上SunJDK提供了兩種模式client compiler(-client)和server
compiler(-server)。
client
compliler又稱(chēng)為C1,較為輕量級(jí),只做少量性能開(kāi)銷(xiāo)比高的優(yōu)化,它占用內(nèi)存較少,適合桌面交互式應(yīng)用,在寄存器分配策略上,JDK6以后采用的為線(xiàn)性?huà)呙杓拇嫫鞣峙渌惴?#xff0c;在其他方面的優(yōu)化主要有:方法內(nèi)嵌、去虛擬化、冗余消除等。
1、方法內(nèi)聯(lián)
對(duì)于java面向?qū)ο蟮恼Z(yǔ)言,通常要調(diào)用多個(gè)方法來(lái)完成功能,執(zhí)行時(shí),要經(jīng)歷多次參數(shù)傳遞,返回值傳遞及跳轉(zhuǎn)等,于是C1采用了方法內(nèi)聯(lián)的方式,即把調(diào)用到的方法的指令直接植入到當(dāng)前的方法中。(可在debug版本的JDK的啟動(dòng)參數(shù)加速-XX:PrintInlining來(lái)查看方法內(nèi)聯(lián)的信息)
2、去虛擬化
去虛擬化是指在裝載class文件后,進(jìn)行類(lèi)層次的分析,如發(fā)現(xiàn)類(lèi)中的方法只提供一個(gè)實(shí)現(xiàn)類(lèi),那么對(duì)于調(diào)用此方法的代碼,也可進(jìn)行方法內(nèi)聯(lián),從而提升執(zhí)行的性能。
3、冗余消除
冗余消除是指在編譯時(shí),根據(jù)運(yùn)行時(shí)狀況進(jìn)行代碼折疊或消除。如某個(gè)判斷條件為false則可將條件內(nèi)的代碼消除。
Server
Compiler又稱(chēng)為C2,較為重量級(jí),C2采用了大量傳統(tǒng)編譯優(yōu)化技巧,占用內(nèi)存會(huì)相對(duì)多,適用于服務(wù)器應(yīng)用。和C1不同的主要是寄存器分配策略和優(yōu)化的范圍,寄存器分配策略上C2采用的為傳統(tǒng)的圖著色寄存器分配算法:由于C2會(huì)收集程序的運(yùn)行信息(收集的信息主要有,分支的跳轉(zhuǎn)/不跳轉(zhuǎn)的頻率,某條指令出新過(guò)的類(lèi)型、是否出現(xiàn)過(guò)控制、是否出現(xiàn)過(guò)一場(chǎng)),因此其優(yōu)化的范圍更多在于全局的優(yōu)化,而不僅僅是一個(gè)方法塊的優(yōu)化。
逃逸分析是C2進(jìn)行很多優(yōu)化的基礎(chǔ),逃逸分析是指根據(jù)運(yùn)行狀況來(lái)判斷方法中的變量是否會(huì)被外部讀取,如不會(huì)則認(rèn)為此變量是逃逸的,基于逃逸分析C2在編譯時(shí)會(huì)做標(biāo)量替換、棧上分配和同步消除等優(yōu)化。
1、標(biāo)量替換
變量替換的意思簡(jiǎn)單來(lái)說(shuō)就是用標(biāo)量替換聚合量。這種方式能帶來(lái)的好處是,如果創(chuàng)建的對(duì)象并未用到其中的全部變量,則可以節(jié)省一定的內(nèi)存。
2、棧上分配
如果變量沒(méi)有逃逸,那么C2會(huì)選擇在棧上直接創(chuàng)建對(duì)象實(shí)例,而不是在JVM堆上,在棧上分配的好處一方面是更加快速,另一方面是回收時(shí)隨著方法的結(jié)束,對(duì)象也被回收了。
3、同步消除
同步消除是指如果發(fā)現(xiàn)同步的對(duì)象未逃逸,那也沒(méi)有同步的必要了,在C2編譯時(shí)會(huì)直接去掉同步。
除了C1\C2外,還有一種較為特殊的編譯:OSR(On
Stack
Replace)。OSR和C1、C2的最主要的不同點(diǎn)在于OSR編譯只替換循環(huán)代碼體的入口,而C1、C2替換的是方法調(diào)用的入口,因此在OSR編譯后會(huì)出現(xiàn)的現(xiàn)象是方法的整段代碼都被編譯了,但在只有循環(huán)代碼體部分才執(zhí)行編譯后的機(jī)器碼,其他部分仍然是是解釋執(zhí)行方法。
默認(rèn)情況下,SunJDK根據(jù)機(jī)器配置來(lái)選擇C1或C2模式,當(dāng)機(jī)器配置CPU超過(guò)2核且內(nèi)存超過(guò)2GB及默認(rèn)為C2模式,但在32位機(jī)器上時(shí)鐘選擇的都是C1模式,也可在啟動(dòng)時(shí)通過(guò)增加-client或-server來(lái)強(qiáng)制指定。
SunJDK為提升程序執(zhí)行性能,在C1好C2上做了很多努力,其他各種實(shí)現(xiàn)的JVM也在編譯執(zhí)行上做了很多的優(yōu)化,SunJDK之所以未選擇在啟動(dòng)時(shí)即編譯成機(jī)器碼,主要是因?yàn)?#xff1a;靜態(tài)編譯并不能根據(jù)程序的運(yùn)行狀況來(lái)優(yōu)化執(zhí)行的代碼,C2這種方式是根據(jù)運(yùn)行狀況來(lái)進(jìn)行動(dòng)態(tài)編譯的,如分支判斷、逃逸分析等,這些措施會(huì)提升程序執(zhí)行的性能,在靜態(tài)編譯的情況下是無(wú)法實(shí)現(xiàn)的,給C2收集運(yùn)行數(shù)據(jù)越長(zhǎng)時(shí)間,編譯出的代碼也會(huì)越優(yōu);解釋執(zhí)行比編譯執(zhí)行更節(jié)省內(nèi)存;啟動(dòng)時(shí)解釋執(zhí)行的啟動(dòng)速度比編譯在啟動(dòng)更快。
當(dāng)程序在未編譯期間解釋執(zhí)行會(huì)比較慢,因此需要取一個(gè)權(quán)衡值,在SunJDK中主要依據(jù)方法上的兩個(gè)計(jì)數(shù)器是否超過(guò)閾值,其中一個(gè)計(jì)數(shù)器為調(diào)用計(jì)數(shù)器,即方法被調(diào)用的次數(shù);另一個(gè)為回邊計(jì)數(shù)器,即方法中循環(huán)執(zhí)行部分代碼的執(zhí)行次數(shù)。下面介紹兩個(gè)計(jì)數(shù)器對(duì)應(yīng)的閾值
(1)ComplieThresold
該值是指方法被調(diào)用多少次后,就編譯為機(jī)器碼,在client模式下默認(rèn)為1500次,在server模式下為10000次,可通過(guò)在啟動(dòng)時(shí)天機(jī)-XX:CompilerThreshold=10000來(lái)設(shè)置該值
(2)OnStackReplacePercentage
該值用于計(jì)算是否觸發(fā)OSR編譯的閾值,默認(rèn)模式下client為933,server模式下為140,該值可通過(guò)在啟動(dòng)時(shí)添加-XX:OnStackReplacePercetage=140來(lái)設(shè)置。
反射執(zhí)行
反射執(zhí)行是Java的亮點(diǎn)之一,基于反射可動(dòng)態(tài)調(diào)用某對(duì)象實(shí)例中對(duì)應(yīng)的方法、訪(fǎng)問(wèn)查看對(duì)象的屬性等,無(wú)需再編寫(xiě)代碼時(shí)就確定要?jiǎng)?chuàng)建的對(duì)象。這使得Java可以很靈活的實(shí)現(xiàn)對(duì)象的調(diào)用,例如MVC框架中通常要調(diào)用實(shí)現(xiàn)類(lèi)中的execute方法,但框架在編寫(xiě)時(shí)是無(wú)法知道實(shí)現(xiàn)類(lèi)的,在Java中則可以通過(guò)反射機(jī)制直接去調(diào)用應(yīng)用類(lèi)的實(shí)現(xiàn)類(lèi)中的execute方法。
這種方式對(duì)于框架之類(lèi)的代碼而言非常重要,反射和直接創(chuàng)建對(duì)象實(shí)例調(diào)用方法的最大不同在于創(chuàng)建的過(guò)程、方法調(diào)用的過(guò)程是動(dòng)態(tài)的。這也是的采用反射生成執(zhí)行方法調(diào)用的代碼并不像直接調(diào)用實(shí)例對(duì)象代碼,編譯后就可直接生成對(duì)象方法調(diào)用的字節(jié)碼,而是只能生成調(diào)用JVM反射實(shí)現(xiàn)的字節(jié)碼了。
要實(shí)現(xiàn)動(dòng)態(tài)的調(diào)用,最直接的方法就是動(dòng)態(tài)生成字節(jié)碼,并加載到JVM中執(zhí)行,SunJDK采用的即為這種方法。
總結(jié)
以上是生活随笔為你收集整理的java 执行机制_Java类的执行机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 惠斯通电桥信号调理芯片_elmos推出专
- 下一篇: 哈希表数据结构_Java数据结构哈希表如