Java中的四種引用:
1、強(qiáng)引用(Strong Reference):在程序代碼中普遍存在的刊头,類似Object object = new Objec(),只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會回收被引用的對象
2椿访、軟引用(Soft Reference):用來描述還有一些用壳影,但并非必須的對象。對于軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前绿渣,將會把這些對象列入回收范圍之中朝群,
并進(jìn)行第二次回收。如果這次回收還沒有足夠的內(nèi)存中符,才會拋出內(nèi)存溢出異常姜胖。
3、弱引用(Weak Reference):用來描述非必須對象淀散,但是它的強(qiáng)度比軟引用更弱一些右莱,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集器發(fā)生之前。當(dāng)垃圾收集器工作時档插,
無論當(dāng)前內(nèi)存是否足夠慢蜓,都會回收掉被弱引用關(guān)聯(lián)著的對象。
4郭膛、虛引用(Phantom Reference):也稱幽靈引用或幻影引用胀瞪,它使最弱的一種引用關(guān)系。一個對象是否有虛引用的存在完全不會對其生存時間構(gòu)成影響饲鄙,也無法通過虛引用獲得
一個對象實例。為一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是希望該對象被收集器回收時收到一個系統(tǒng)通知圆雁。
判斷一個類是否是無用的類:
1忍级、該類所有的實例都已經(jīng)被回收,也就是java堆中不存在該類的所有實例
2伪朽、加載該類的ClassLoader已經(jīng)被回收
3轴咱、該類對應(yīng)的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法烈涮。
判斷對象是否存活的算法:
1朴肺、引用計數(shù)法
2、根搜索算法
垃圾收集算法:
1坚洽、標(biāo)記-清除
2戈稿、復(fù)制
3、標(biāo)記-整理
垃圾收集器:
1讶舰、Serial收集器:單線程高效的收集器
2鞍盗、ParNew收集器:Serial收集器的多線程版本
3、Parallel Scavenge收集器:新生代收集器跳昼,使用復(fù)制算法般甲,并行的,目標(biāo)是達(dá)到一個可控制的吞吐量鹅颊,由于與吞吐量關(guān)系密切敷存,Parallel Scavenge收集器也被常稱為“吞吐量優(yōu)先”
收集器。
4堪伍、Serail Old收集器:Serial收集器的老年代版本锚烦,同樣是一個單線程收集器
5觅闽、Parallel Old收集器:是Parallel Scavenge的老年代版本,使用多線程和標(biāo)記整理算法挽牢。
6谱煤、CMS收集器:是一種以獲取最短回收停頓時間為目標(biāo)的收集器。
運作過程:
1禽拔、初始標(biāo)記
2刘离、并發(fā)標(biāo)記
3、重新標(biāo)記
4睹栖、并發(fā)清除
優(yōu)點:并發(fā)收集硫惕,低停頓
7、G1收集器
新生代GC(Minor GC)
老年代GC(Major GC/Full GC)
內(nèi)存分配和回收策略:
1野来、對象優(yōu)先在Eden分配
2恼除、大對象直接進(jìn)入老年代:
大對象對虛擬機(jī)的內(nèi)存分配來說是一個壞消息,比遇到一個大對象更壞的消息是遇到一群“朝生熄滅”的大對象曼氛,寫程序的時候應(yīng)該盡量避免豁辉。
-XX:PretenureSizeThreshold參數(shù),令大于這個值的對象直接在老年代中分配舀患。這樣做避免在Eden去和連個servivor區(qū)之間發(fā)生大量的內(nèi)存拷貝徽级。
3、長期存活的對象將進(jìn)入老年代
4聊浅、動態(tài)對象年齡判定
虛擬機(jī)并總是要求對象經(jīng)過設(shè)定的年齡閾值后才進(jìn)入老年代餐抢,如果在survivor空間中相同年齡所有對象大小的總和大于survivor空間的一半,年齡大于或等于該
年齡的對象就可以直接進(jìn)入老年代低匙,無須等到MaxTenuringThreshold要求的年齡旷痕。
5、空間分配擔(dān)保:
在發(fā)生Minor GC時顽冶,虛擬機(jī)會檢測之前每次晉升到老年代的平均大小是否大于老年代的剩余空間大小欺抗,如果大于,則改為直接進(jìn)行一次Full GC,如果允許强重,那只會進(jìn)行
Minor GC,如果不允許則也要改為進(jìn)行一次Full GC佩迟。
虛擬機(jī)執(zhí)行子系統(tǒng):
Class類文件結(jié)構(gòu):
1、魔數(shù)和Class文件的版本
魔數(shù):用于確定該文件是否是一個能被虛擬機(jī)接收的Class文件竿屹,其值為0xCAFEBABE报强。
進(jìn)階著魔數(shù)的四個字節(jié)之后的2個字節(jié)是次版本號,再接著的2個字節(jié)是主版本號拱燃。
2秉溉、常量池
緊接著朱版本號是常量池入口
3、訪問標(biāo)識
在常量池結(jié)束后,緊接著的2個字節(jié)代表訪問標(biāo)識召嘶。這個標(biāo)識用于識別一些類或者接口層次的訪問信息父晶,包括:這個Class是類還是接口,是否
定義為public類型弄跌,是否定義為abstract類型甲喝,如果是類的話是否被聲明為final等等。
4铛只、類索引埠胖、父類索引、接口索引集合
由這三項來確定類的繼承關(guān)系淳玩。
5直撤、字段表集合
用于描述或者類中聲明的變量。這些信息包括:變量的作用域蜕着,類級別還是實例級別谋竖,可見性,并發(fā)可見性承匣,是否可序列化蓖乘,字段數(shù)據(jù)類型,字段名稱
6韧骗、方法表集合
與字段表集合的描述方法一致
依次包括了訪問標(biāo)識驱敲、名稱索引、描述符索引宽闲、屬性表集合幾項。
7握牧、屬性表集合
在Class文件容诬、字段表和方法表中都可以攜帶自己的屬性集合,用以描述某些場景專有的信息沿腰。
1览徒、code屬性:
Java程序方法體里面的代碼經(jīng)過Javac編譯器處理后,最終變?yōu)樽止?jié)碼指令存儲在code屬性內(nèi)颂龙。
如果把一個Java程序里的信息分為代碼和元數(shù)據(jù)习蓬,那么在整個class文件里,code屬性用于描述代碼措嵌,所有的其他數(shù)據(jù)項目就是用于描述元數(shù)據(jù)躲叼。
2、Exceptions屬性
作用是列出方法中可能拋出的受檢查異常企巢,也就是方法描述時在throws關(guān)鍵字后面列舉的異常枫慷。
3、LineNumberTable屬性
用以描述源碼行號和字節(jié)碼行號的對應(yīng)關(guān)系
4、LocalVarialableTable屬性
用以描述棧幀中局部變量表中的變量與Java源代碼中定義的變量之間的關(guān)系或听,它不是運行時必須的屬性探孝。
5、SourceFile屬性
用以記錄生成這個Class文件的源碼文件名稱
6誉裆、ConstantValue屬性
通知虛擬機(jī)自動為靜態(tài)變量賦值顿颅。只有被靜態(tài)關(guān)鍵字(static)修改的變量才可以使用這項屬性
7、innerClasses屬性
用以記錄內(nèi)部類和宿主類之間的關(guān)聯(lián)
8足丢、Depreated及Synthetic屬性
都是布爾屬性粱腻,只存在有和沒有的區(qū)別,沒有屬性值的概念霎桅。
synthetic代表此字段不是由java源碼直接產(chǎn)生的栖疑,而是由編譯器自行添加的。
Class文件結(jié)構(gòu)的發(fā)展:
類加載的時機(jī):
類的生命周期包括:加載滔驶、驗證遇革、準(zhǔn)備、解析揭糕、初始化萝快、使用和卸載,其中驗證著角、準(zhǔn)備和解析稱為連接揪漩。
虛擬機(jī)規(guī)范嚴(yán)格規(guī)定了有且只有四種情況必須對類進(jìn)行初始化:
1、使用new關(guān)鍵字實例化對象的時候吏口、讀取或設(shè)置類的靜態(tài)字段以及調(diào)用一個類的靜態(tài)方法的時候
2奄容、使用java.lang.reflect包的方法對類進(jìn)行反射調(diào)用的時候,如果類沒有初始化产徊,則必須先對其進(jìn)行初始化
3昂勒、當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有初始化舟铜,則會觸發(fā)父類的初始化
4戈盈、當(dāng)虛擬機(jī)啟動時,用戶需要指定一個要執(zhí)行的主類(包含main方法的那個類)谆刨,虛擬機(jī)會先初始化這個主類
以上4中情況稱為主動引用塘娶,除此之外的所有引用都不會初始化類,稱為被動引用痊夭。
被動引用舉例如下:
1刁岸、通過子類引用父類的靜態(tài)字段,不會觸發(fā)子類的初始化她我。對于靜態(tài)字段难捌,只有直接定義這個字段的類才會被初始化膝宁,通過子類來引用父類,只會觸發(fā)父
類的初始化根吁,不會觸發(fā)子類的初始化员淫。
2、通過數(shù)組定義來引用類不會觸發(fā)此類的初始化
TestClass[] testArray = new TestClass[10];
3击敌、常量在編譯階段會存入調(diào)用類的常量池中介返,本質(zhì)上沒有引用到定義常量的類,因此不會觸發(fā)定義常量的類的初始化沃斤。
接口的初始化過程與類是一致的圣蝎,接口初始化與類的初始化真正的區(qū)別是:當(dāng)一個類在初始化時,要求其父類已經(jīng)全部初始化過了衡瓶,但是一個接口在初始化時
并不要求其父接口全部完成了初始化徘公,只有在真正使用到父接口的時候才初始化。
類加載的過程:
1哮针、加載
在加載階段关面,虛擬機(jī)要完成以下三件事情:
1、通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流
2十厢、將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)
3等太、在Java堆中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個數(shù)據(jù)的訪問入口
2蛮放、驗證
確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求缩抡,并且不會危害虛擬機(jī)自身的安全。
驗證的大致步驟:
1包颁、文件格式驗證:
驗證字節(jié)流是否符合Class文件格式的規(guī)范瞻想,并且能被當(dāng)前版本的虛擬機(jī)處理。
驗證完成后娩嚼,字節(jié)流會進(jìn)入方法區(qū)進(jìn)行存儲蘑险。所以后面的驗證全部是基于方法區(qū)的存儲結(jié)構(gòu)進(jìn)行的
2、元數(shù)據(jù)驗證
對字節(jié)碼描述的信息進(jìn)行語義分析待锈,以保證其描述的信息符合Java語言規(guī)范的要求。
可能包含的驗證點如下:
這個類是否有父類
這個類是否繼承了不允許被繼承的類
如果這個類不是抽象類嘴高,是否實現(xiàn)了其父類或接口中要求實現(xiàn)的所有方法
類中的字段方法是否與父類產(chǎn)生了矛盾
...
3竿音、字節(jié)碼驗證
是整個過程中最復(fù)雜的階段,主要工作是進(jìn)行數(shù)據(jù)流和控制流分析拴驮。
在第二階段中對元數(shù)據(jù)信息中的數(shù)據(jù)類型做完校驗后春瞬,這階段對類的方法體進(jìn)行校驗分析。
這階段的任務(wù)是保證被校驗類的方法在運行不會做出危害虛擬機(jī)安全的行為套啤。
4宽气、符號引用驗證
符號引用驗證可以看成是對類自身以外(常量池中的各種符號引用)的信息進(jìn)行匹配性的校驗随常,通常需要校驗以下內(nèi)容:
符號引用中通過字符串描述的全限定名是否能找到對應(yīng)的類
在指定類中是否能找到符合方法的字段描述及簡單名稱所描述的方法和字段。
符號引用中的類萄涯、字段和方法的訪問性是否可被當(dāng)前類訪問
...
符號引用驗證是確保解析能夠正常進(jìn)行绪氛,如果無法通過符號引用驗證,則會拋出一個異常
如果全部代碼全部驗證過涝影,可以通過-Xverify:none參數(shù)來關(guān)閉大部分的類驗證措施枣察,以縮短虛擬機(jī)類加載時間
3、準(zhǔn)備
準(zhǔn)備階段正式為類分配內(nèi)存并設(shè)置類變量初始值的階段燃逻,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配序目。
這時候會將靜態(tài)變量的值賦值為默認(rèn)初始值,如果靜態(tài)變量是一個常量則會立即給其賦值
4伯襟、解析
解析階段是虛擬機(jī)將常量池中的符號引用替換為直接引用的過程猿涨。
解析動作主要針對類或接口、字段姆怪、類方法叛赚、接口方法四類符號引用進(jìn)行。
1片效、類或接口的解析
如果當(dāng)前類為D红伦,將從未解析過的符號引用N解析為一個類或接口C的引用,包括以下3個步驟:
1淀衣、如果C不是一個數(shù)組昙读,JVM就會把代表N的全限定名傳遞給D的類加載器去加載這個C類,一旦過程出現(xiàn)任何異常膨桥,解析過程宣告失敗蛮浑。
2、如果C是一個數(shù)組類型只嚣,并且數(shù)組的元素類型為對象,那么久會按照第一點加載數(shù)組元素類型册舞。
3、如果上面步驟有效進(jìn)行调鲸,那么C在虛擬機(jī)中實際上已經(jīng)成為一個有效的類或接口了,但是在解析完成之前還要進(jìn)行符號引用驗證藐石,
確認(rèn)C是否具備對D的訪問權(quán)限,如果發(fā)現(xiàn)不具備訪問權(quán)限于微,則會拋出java.lang.IllegalAccessError異常
2青自、字段解析
要解析一個未被解析的字段符號引用,首先解析字段所屬的類或接口的符號引用延窜。解析完成后按照如下要求對C進(jìn)行后續(xù)字段搜索:
先查找本身,然后查找繼承關(guān)系中的類或接口需曾,如果還未找到則會拋出java.lang.NoSuchFieldError異常
。
如果查找過程成功返回引用祈远,則會對這個字段進(jìn)行權(quán)限驗證呆万,如果發(fā)現(xiàn)不具備對字段的訪問權(quán)限,將拋出java.lang.IllegalAccessError異常车份。
3谋减、類方法解析
先解析方法所屬的類或接口。然后按照如下要求對后續(xù)方法進(jìn)行搜索:
先查找本身扫沼,然后查找繼承關(guān)系中的父類
如果找到方法出爹,則對其進(jìn)行權(quán)限驗證,同第二步
4缎除、接口方法解析
先解析方法所屬的類或接口严就。然后按如下要求對后續(xù)方法進(jìn)行搜索:
先查本身,然后再查父接口
5器罐、初始化
初始化階段是執(zhí)行類構(gòu)造器clinit()方法的過程梢为,clinit()方法是編譯器自動收集類中所有類變量的賦值動作和靜態(tài)語句塊中的語句合并產(chǎn)生的。
虛擬機(jī)會保證一個類的clinit方法在多線程環(huán)境中被正確的加鎖和同步轰坊,如果在clinit方法中有耗時很長的操作铸董,那就有可能造成多個線程阻塞,
在實際中這種阻塞往往是很隱蔽的肴沫。
類加載器:
比較兩個類是否相等粟害,只有在這兩個類是由同一個類加載器加載的前提下才有意義,否則颤芬,即使這2個類來源于同一個Class文件悲幅,只要加載它們的類
加載器不同,那這兩個類就必定不相等站蝠。
雙親委派模型:
站在Java虛擬機(jī)的角度講汰具,只存在兩種不同的類加載器:一種是啟動類加載器(Bootstrap ClassLoader),這個加載器是用C++語言實現(xiàn)沉衣,是虛擬機(jī)
自身的一部分郁副,另外一種就是所有其他的類加載器减牺,這些類加載器都是由java語言實現(xiàn)豌习,獨立于虛擬機(jī)外部存谎,并且全部都繼承自抽象類lava.lang.ClassLoader。
從Java開發(fā)人員的角度看肥隆,類加載器還可以劃分的更細(xì)一點既荚,絕大部分java程序都會使用到以下三種系統(tǒng)提供的類加載器:
1恰聘、啟動類加載器(BootStrap ClassLoader)
負(fù)責(zé)將放在<JAVA_HOME>/lib目錄下的吸占,或者被-Xbootclasspath參數(shù)所指定的路徑中的矾屯,并且是虛擬機(jī)識別的(僅僅按文件名識別,如rt.jar孙技,名字
不符合的類庫即使放在lib目錄中也不會被加載)類庫加載到虛擬機(jī)內(nèi)存中牵啦。啟動類加載器無法被Java程序直接引用妄痪。
2、擴(kuò)展類加載器(Extension ClassLoader)
這個加載器由sum.misc.Launcher$ExtClassLoader實現(xiàn)僧著,它負(fù)責(zé)加載<JAVA_HOME>/lib/ext目錄中的盹愚,或者被java.ext.dirs系統(tǒng)變量所指定的
路徑中的所有類庫站故,開發(fā)者可以直接使用擴(kuò)展類加載器西篓。
3、應(yīng)用程序類加載器(Application ClassLoader)
這個類加載器由sun.misc.Launcher$AppClassLoader來實現(xiàn)虱黄。由于這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值橱乱,所以
一般也稱它為系統(tǒng)類加載器。它負(fù)責(zé)加載用戶類路徑(ClassPath)上所指定的類庫作瞄,開發(fā)者可以直接使用這個類加載器危纫,如果應(yīng)用程序中沒有
自定義過自己的類加載器种蝶,一般情況下這個就是程序中默認(rèn)的類加載器。
雙親委派模型要求除了頂層的啟動類加載器外宵喂,其余的加載器都應(yīng)當(dāng)有自己的父類加載器锅棕,這里類加載器之間的父子關(guān)系一般不會以繼承的關(guān)系來實現(xiàn)淌山,
都是使用組合關(guān)系來復(fù)用父加載器。
雙親委派模型的工作過程:
如果一個類加載器收到一個加載請求德绿,它首先不會自己去嘗試加載這個類移稳,而是把這個請求委派給父加載器去完成个粱,每一個層次的加載器都是如此翻翩,
因此所有的加載請求最終都傳送到頂層的啟動類加載器中,只有當(dāng)父加載器反饋自己無法完成這個加載請求(它的搜索范圍沒有找到這個類)時胶征,
子加載器才會嘗試自己去加載睛低。
虛擬機(jī)字節(jié)碼執(zhí)行引擎:
運行時棧幀結(jié)構(gòu):
棧幀是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)钱雷。它使虛擬機(jī)運行數(shù)據(jù)區(qū)中的虛擬機(jī)棧的棧元素。
每一個棧幀都包含了局部變量表、操作數(shù)棧瘪校、動態(tài)連接阱扬、方法返回地址和一些額外的附加信息。
一個線程中方法調(diào)用鏈可能會很長馍刮,很多方法都同時處于執(zhí)行狀態(tài)卡啰。對于執(zhí)行引擎來講警没,活動線程中,只有棧頂?shù)臈怯行У耐隽常Q為當(dāng)前棧幀浅碾,這個棧幀所
關(guān)聯(lián)的方法稱為當(dāng)前方法续语。執(zhí)行引擎所運行的所有字節(jié)碼指令都只針對當(dāng)前棧幀進(jìn)行操作疮茄。
1、局部變量表
2焚虱、操作數(shù)棧
3鹃栽、動態(tài)連接
4、方法返回地址
當(dāng)一個方法被執(zhí)行后薇芝,有2種方式退出這個方法:
1夯到、執(zhí)行引擎遇到任何一個方法返回的字節(jié)碼指令饮亏,稱為正常完成出口
2路幸、在方法執(zhí)行過程中遇到了異常,并且這個異常咩有在方法體內(nèi)得到處理晃听,這個退出稱為異常完成出口能扒,此時是不會給上層調(diào)用者任何返回值的辫狼。
方法退出的過程實際上等同于把當(dāng)前棧幀出棧,因此退出時可能執(zhí)行的操作有:
恢復(fù)上層方法的局部變量表和操作數(shù)棧越平,把返回值(如果有的話)壓入調(diào)用者棧幀的操作數(shù)棧中秦叛,調(diào)整PC計數(shù)器的值以指向方法調(diào)用指令后面的一條指令瀑粥。
5狞换、附加信息:
方法調(diào)用:
Class文件的編譯過程不包含傳統(tǒng)編譯中的連接步驟,一切方法調(diào)用在Class文件里面存儲的都是符號引用查库,而不是在實際方法運行時內(nèi)存布局中的入口地址(
相當(dāng)于之前說的直接引用)樊销。這個特性帶來了java更強(qiáng)大的動態(tài)擴(kuò)展能力,但也是Java方法的調(diào)用過程變得相對復(fù)雜起來裤园,需要再類加載期間甚至在運行期間
才能確定目標(biāo)方法的直接引用拧揽。
1腺占、解析:
將“編譯器可知湾笛,運行期不可變”的方法的符號引用轉(zhuǎn)化為直接引用嚎研,符合這個要求的方法有:靜態(tài)方法和私有方法库倘。
java虛擬機(jī)提供了四條方法調(diào)用字節(jié)碼指令:
1教翩、invokestatic;調(diào)用靜態(tài)方法
2蚜退、invokespecial:調(diào)用實例構(gòu)造器<init>方法钻注、私有方法和父類方法
3配猫、invokevirtual:調(diào)用所有的虛方法
4泵肄、invokeinterface:調(diào)用接口方法,會在運行時再確定一個實現(xiàn)此接口的對象
2品追、分派
1诵盼、靜態(tài)分派
2、動態(tài)分派
在運行期根據(jù)實際類型確定方法的運行版本稱為動態(tài)分派
3洁墙、單分派和多分派
方法的接受者和方法的參數(shù)統(tǒng)稱為方法的宗量热监。
單分派是根據(jù)一個宗量對目標(biāo)方法進(jìn)行選擇饮寞,多分派則是根據(jù)多于一個的宗量對目標(biāo)方法進(jìn)行選擇幽崩。
java語言是一門靜態(tài)多分配、動態(tài)單分派的語言陌选。
4蹄溉、虛擬機(jī)動態(tài)分派的實現(xiàn)
基于棧的字節(jié)碼解釋執(zhí)行引擎:
虛擬機(jī)是如何執(zhí)行方法中的字節(jié)碼指令的柒爵。
1、解釋執(zhí)行:
在java語言中法瑟,javac編譯器完成了程序代碼經(jīng)過詞法分析瓢谢、語法分析到抽象語法樹驮瞧,再遍歷抽象語法樹生成線性字節(jié)碼指令流的過程论笔。
因為這一部分動作是虛擬機(jī)外執(zhí)行的,而解釋器在虛擬機(jī)的內(nèi)部蒜埋,所以Java程序的編譯就是半獨立的實現(xiàn)整份。
2、基于棧的指令集和基于寄存器的指令集:
棧架構(gòu)指令集慢與寄存器指令集
3烈评、基于棧的解釋器執(zhí)行過程
類加載以及執(zhí)行子系統(tǒng)案例與實戰(zhàn):
案例分析:
1讲冠、tomcat:正統(tǒng)的類加載器架構(gòu)
2、OSGI:靈活的類加載器架構(gòu)
3谱仪、字節(jié)碼生成技術(shù)和動態(tài)代理的實現(xiàn)
4疯攒、Retrotranslator:跨越JDK的版本
程序編譯與代碼優(yōu)化:
早期(編譯器)優(yōu)化:
從計算機(jī)程序出現(xiàn)的那一天起列荔,對效率的追逐就是程序天生的堅定的信仰肌毅,這個過程猶如一場沒有終點姑原、永不停歇的方程式競賽,程序猿是車手笨奠,
技術(shù)平臺則是賽道上飛馳的賽車般婆。
前端編譯器:Sun的javac朵逝、Eclipse JDT中的增量式編譯器(ECJ)
JIT編譯器:HotSpot VM的C1配名、C2編譯器
AOT編譯器:GUN Compiler for Java(GCJ)、Excelsior JET
晚期(運行期)優(yōu)化:
Java程序最終是通過解釋器進(jìn)行解釋執(zhí)行的宇整,當(dāng)虛擬機(jī)發(fā)現(xiàn)某個方法或代碼塊的運行特別頻繁鳞青,就會把這些代碼認(rèn)定為“熱點代碼”,為了提高熱點
代碼的執(zhí)行效率臂拓,在運行時埃儿,虛擬機(jī)將會把這些代碼編譯成與本地平臺相關(guān)的機(jī)器碼,并進(jìn)行各種層次的優(yōu)化精钮,完成這個任務(wù)的編譯被稱為即時編
器(Just In Time Compiler轨香,JIT編譯器)幼东。
編譯器優(yōu)化技術(shù):
方法內(nèi)聯(lián)
冗余訪問消除
復(fù)寫傳播
無用代碼消除
公共子表達(dá)式消除
如果一個表達(dá)式已經(jīng)計算過了根蟹,并且從先前的計算到現(xiàn)在E中的所有變量值偶讀沒有發(fā)生變化,那么E的這次出現(xiàn)就稱為了公共子表達(dá)式球散。
對于這種表達(dá)式?jīng)]有必要再花時間進(jìn)行計算蕉堰,只要需要直接用前面計算過的表達(dá)式結(jié)果代替E就可以了悲龟。
數(shù)組邊界消除檢查
如果編譯器只要通過數(shù)據(jù)流分析就可以判定循環(huán)變量的取值范圍永遠(yuǎn)在區(qū)間[0, foo.length)之內(nèi),那整個循環(huán)過程中就可以數(shù)組上下界
檢查消除掉皿渗,這可以節(jié)省很多次的條件判斷操作乐疆。與語言相關(guān)的其他消除操作還有不少约计,如自動裝箱消除煤蚌、安全點消除细卧、消除反射等贪庙。
方法內(nèi)聯(lián)
逃逸分析
是目前java虛擬機(jī)中比較前沿的優(yōu)化技術(shù)止邮,它與類型繼承關(guān)系分析一樣奏窑,并不是直接優(yōu)化代碼的手段,而是為其他優(yōu)化手段提供依據(jù)的分
析技術(shù)撩匕。
分析對象動態(tài)作用域:當(dāng)一個對象被定義后止毕,它可能被外部方法所引用
如果能證明一個對象不會逃逸到方法或線程外漠趁,則可能為這個變量進(jìn)行一些高效的優(yōu)化:
棧上分配
同步消除
線程同步是一個耗時的過程,如果該對象不會逃逸闯传,那么久不會有競爭丸边,對這個變量實施的同步就可以消除了荚孵。
標(biāo)量替換
標(biāo)量是指一個數(shù)據(jù)無法再分成更小的數(shù)據(jù)來表示了,java虛擬機(jī)中的原始數(shù)據(jù)類型都不能再進(jìn)一步分解骄呼,它們
就可以成為標(biāo)量蜓萄。相對的澄峰,如果一個數(shù)據(jù)可以繼續(xù)分解俏竞,就稱為聚合量堂竟,java中的對象就是最典型的聚合量出嘹。
如果把一個java對象拆散税稼,根據(jù)程序訪問的情況垮斯,將其使用到成員變量恢復(fù)原始類型來訪問就叫標(biāo)量替換。
對Java編譯器的深入了解丸升,有助于在工作中分辨哪些代碼是編譯器可以幫我們處理的狡耻,哪些代碼需要自己調(diào)節(jié)以便更適合編譯器的優(yōu)化猴凹。