中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

關(guān)于Java性能的9個(gè)謬論

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

Java的性能有某種黑魔法之稱。部分原因在于Java平臺(tái)非常復(fù)雜,很多情況下問題難以定位。然而在歷史上還有一種趨勢,人們靠智慧和經(jīng)驗(yàn)來研究Java性能,而不是靠應(yīng)用統(tǒng)計(jì)和實(shí)證推理。在這篇文章中,我希望拆穿一些最荒謬的技術(shù)神話。

1.Java很慢

關(guān)于Java的性能有很多謬論,這一條是最過時(shí)的,可能也是最為明顯的。

確實(shí),在上世紀(jì)90年代和本世紀(jì)初處,Java有時(shí)是很慢。

然而從那以后,虛擬機(jī)和JIT技術(shù)已經(jīng)有了十多年的改進(jìn),Java的整體性能現(xiàn)在已經(jīng)非常好了。

在6個(gè)獨(dú)立的Web性能基準(zhǔn)測試中,Java框架在24項(xiàng)測試中有22項(xiàng)位列前四。

盡管JVM利用性能剖析僅優(yōu)化常用的代碼路徑,但這種優(yōu)化效果很明顯。很多情況下,JIT編譯的Java代碼和C++一樣快,而且這樣的情況越來越多了。

盡管如此,依然有人認(rèn)為Java平臺(tái)很慢,這或許源自體驗(yàn)過Java平臺(tái)早期版本的人的歷史偏見。

在下結(jié)論之前,我們建議保持客觀的態(tài)度,并且評(píng)估一下最新的性能結(jié)果。

2.可以孤立地看待單行Java代碼

考慮下面這行短小的代碼:

MyObject obj = new MyObject();

對(duì)Java開發(fā)者而言,看似很明顯,這行代碼一定會(huì)分配一個(gè)對(duì)象并調(diào)用適當(dāng)?shù)臉?gòu)造器。

我們也許可以據(jù)此推出性能邊界了。我們認(rèn)為這行代碼一定會(huì)導(dǎo)致執(zhí)行一定量的工作,基于這種推定,就可以嘗試計(jì)算其性能影響了。

其實(shí)這種認(rèn)識(shí)是錯(cuò)誤的,它讓我們先入為主地認(rèn)為,不管什么工作,在任何情況下都會(huì)進(jìn)行。

事實(shí)上,javac和JIT編譯器都能夠?qū)⑺来a優(yōu)化掉。就JIT編譯器而言,基于性能剖析數(shù)據(jù),甚至可以通過預(yù)測將代碼優(yōu)化掉。在這樣的情況下,這行代碼根本不會(huì)運(yùn)行,所以不會(huì)影響性能。

此外,在某些JVM中——比如JRockit——JIT編譯器甚至可以將對(duì)象上的操作分解,這樣即便代碼路徑還有效,分配操作也可以避免。

這里的寓意是,在處理Java性能問題時(shí),上下文非常重要,過早的優(yōu)化有可能產(chǎn)生違反直覺的結(jié)果。所以最好不好過早優(yōu)化。相反,應(yīng)該總是構(gòu)建代碼,并且使用性能調(diào)校技術(shù)來定位性能熱點(diǎn),然后加以改進(jìn)。

3.微基準(zhǔn)測試和你想象的一樣

正如我們上面看到的那樣,檢查一小段代碼不如分析應(yīng)用的整體性能來的準(zhǔn)確。

盡管如此,開發(fā)者還是喜歡編寫微基準(zhǔn)測試。似乎對(duì)平臺(tái)底層的某些方面進(jìn)行修修補(bǔ)補(bǔ)會(huì)帶來無窮的樂趣。

理查德·費(fèi)曼曾經(jīng)說過:“不要欺騙自己,你自己正是最容易被欺騙的人!边@句話用來說明編寫Java微基準(zhǔn)測試這件事是再合適不過了。

編寫良好的微基準(zhǔn)測試極其困難。Java平臺(tái)非常復(fù)雜,而且很多微基準(zhǔn)測試只能用于測量瞬時(shí)效應(yīng),或是Java平臺(tái)的其他意想不到的方面。

例如,如果沒有經(jīng)驗(yàn),編寫的微基準(zhǔn)測試往往就是測一下時(shí)間或垃圾收集,卻沒有抓住真正的影響因素。

只有那些有實(shí)際需求的開發(fā)者和開發(fā)團(tuán)隊(duì)才應(yīng)該編寫微基準(zhǔn)測試。這些基準(zhǔn)測試應(yīng)該完全公開(包括源代碼),而且是可以復(fù)現(xiàn)的,還應(yīng)接受同行評(píng)審及進(jìn)一步的審查。

Java平臺(tái)的很多優(yōu)化表明統(tǒng)計(jì)運(yùn)行和單次運(yùn)行對(duì)結(jié)果影響很大。要得到真實(shí)可靠的答案,應(yīng)該將一個(gè)單獨(dú)的基準(zhǔn)測試運(yùn)行多次,然后把結(jié)果匯總到一起。

如果讀者感覺有必要編寫微基準(zhǔn)測試,Georges、Buytaert和Eeckhout等人的論文《利用嚴(yán)格的統(tǒng)計(jì)方法評(píng)測Java 性能(Statistically Rigorous Java Performance Evaluation)》是個(gè)不錯(cuò)的開始。缺乏適當(dāng)?shù)慕y(tǒng)計(jì)分析,我們很容易被誤導(dǎo)。

有很多開發(fā)好的工具以及圍繞這些工具的社區(qū)(比如Google的Caliper)。如果確實(shí)有必要編寫微基準(zhǔn)測試,那也不要自己編寫,這時(shí)需要的是同行的意見和經(jīng)驗(yàn)。

4.算法慢是性能問題的最常見原因

在開發(fā)者之間有一個(gè)很常見的認(rèn)知錯(cuò)誤(普通大眾也是如此),即認(rèn)為系統(tǒng)中他們控制的那部分很重要。

在探討Java性能時(shí),這種認(rèn)知錯(cuò)誤也有所體現(xiàn):Java開發(fā)者認(rèn)為算法的質(zhì)量是性能問題的主要原因。開發(fā)者考慮的是代碼,因此他們自然會(huì)偏向于考慮自己的算法。

實(shí)際上在處理一系列現(xiàn)實(shí)中的性能問題時(shí),人們發(fā)現(xiàn)算法設(shè)計(jì)是根本問題的幾率不足10%。

相反,與算法相比,垃圾收集、數(shù)據(jù)庫訪問和配置錯(cuò)誤導(dǎo)致應(yīng)用程序緩慢的可能性更大。

大部分應(yīng)用處理的數(shù)據(jù)量相對(duì)較小,因此,即使主要算法效率不高,通常也不會(huì)導(dǎo)致嚴(yán)重的性能問題。可以肯定,我們的算法不是最優(yōu)的;盡管如此,算法帶來的性能問題還是算小的,更多性能問題是應(yīng)用棧的其他部分導(dǎo)致的。

因此我們的最佳建議是,使用實(shí)際生產(chǎn)數(shù)據(jù)來揭開性能問題的真正原因。要測量性能數(shù)據(jù),而不是憑空猜測!

5.緩存可以解決所有問題

“計(jì)算機(jī)科學(xué)中的所有問題都可以通過引入一個(gè)中間層來解決!

David Wheeler的這句程序員格言(在互聯(lián)網(wǎng)上,這句話至少還被認(rèn)為是其他兩位計(jì)算機(jī)科學(xué)家說的)非常常見,尤其是在Web開發(fā)者之中很流行。

如果未能透徹理解現(xiàn)有的架構(gòu),而且分析也已停頓,往往就是“緩存可以解決所有問題”這種謬論抬頭的時(shí)候了。

在開發(fā)者看來,與其處理嚇人的現(xiàn)有系統(tǒng),還不如在前面加一層緩存,將現(xiàn)有系統(tǒng)隱藏起來,以此期待最好的情況。無疑,這種方式只是讓整體架構(gòu)更復(fù)雜了,當(dāng)下一個(gè)接手的開發(fā)者打算了解系統(tǒng)現(xiàn)狀時(shí),情況會(huì)更糟糕。

規(guī)模龐大、設(shè)計(jì)拙劣的系統(tǒng)往往缺乏整體的設(shè)計(jì),是一次一行代碼、一個(gè)子系統(tǒng)這樣寫出來的。然而很多情況下,簡化并重構(gòu)架構(gòu)會(huì)帶來更好的性能,而且?guī)缀蹩偸歉菀鬃屓死斫狻?/p>

所以當(dāng)評(píng)估是否真的有必要加入緩存時(shí),應(yīng)該先計(jì)劃收集一些基本的使用統(tǒng)計(jì)信息(比如命中率和未命中率等),以此證明緩存層帶來的真正價(jià)值。

6.所有應(yīng)用都需要關(guān)注Stop-The-World問題

Java平臺(tái)存在一個(gè)無法改變的事實(shí):為運(yùn)行垃圾收集,所有應(yīng)用線程必須周期性停頓。有時(shí)這被當(dāng)作Java的一個(gè)嚴(yán)重缺點(diǎn),即使沒有任何真憑實(shí)據(jù)。

實(shí)證研究表明,如果數(shù)字?jǐn)?shù)據(jù)(如價(jià)格波動(dòng))變化的頻率超過200毫秒一次,人就無法正常感知了。

應(yīng)用主要是給人用的,因此我們有一個(gè)有用的經(jīng)驗(yàn)法則,200毫秒或低于200毫秒的Stop-The-World(STW)通常是沒有影響的。有些應(yīng)用可能有更高的要求(如流媒體),但很多GUI應(yīng)用是不需要的。

少數(shù)應(yīng)用(比如低延遲交易或機(jī)械控制系統(tǒng))無法接受200毫秒的停頓。除非編寫的就是這類應(yīng)用,否則用戶基本感覺不到垃圾收集器的影響。

值得一提的是,在應(yīng)用線程數(shù)量超過物理核數(shù)的任何系統(tǒng)中,操作系統(tǒng)必須控制對(duì)CPU的分時(shí)訪問。Stop-The-World聽著可怕,但實(shí)際上任何應(yīng)用(不管是JVM還是其他應(yīng)用)都要面對(duì)稀缺計(jì)算資源的爭用問題。

如果不去測量,JVM對(duì)應(yīng)用性能有何附加影響是不清楚的。

總之,請(qǐng)打開GC日志,以此來確定停頓時(shí)間是否真的影響了應(yīng)用。通過分析日志來確定停頓時(shí)間,這里既可以手工分析,也可以利用腳本或工具分析。然后再判定它們是否真的給應(yīng)用于帶來了問題。最重要的是,問自己一個(gè)關(guān)鍵的問題:確實(shí)有用戶抱怨嗎?

7.手寫對(duì)象池適合一大類應(yīng)用

認(rèn)為Stop-The-World停頓在某種程度上是不好的,應(yīng)用開發(fā)團(tuán)隊(duì)的一個(gè)常見反應(yīng)就是在Java堆內(nèi)實(shí)現(xiàn)自己的內(nèi)存管理技術(shù)。這往往會(huì)歸結(jié)為實(shí)現(xiàn)一個(gè)對(duì)象池(甚至是全面的引用計(jì)數(shù)),而且需要使用了領(lǐng)域?qū)ο蟮娜魏未a都參與進(jìn)來。

這種技術(shù)幾乎總是具有誤導(dǎo)性的。它基于過去的認(rèn)知,那時(shí)對(duì)象分配非常昂貴,而修改對(duì)象則廉價(jià)的多,F(xiàn)在的情況已經(jīng)完全不同了。

現(xiàn)在的硬件在分配時(shí)非常高效;最新的桌面或服務(wù)器硬件,內(nèi)存帶寬至少是2到3GB。這是一個(gè)很大的數(shù)字,除非專門編寫的應(yīng)用,否則要充分利用這么大的帶寬還真不容易。

一般來說,正確實(shí)現(xiàn)對(duì)象池非常困難(尤其是有多個(gè)線程工作時(shí)),而且對(duì)象池還帶來了一些負(fù)面的要求,使這種技術(shù)不是一個(gè)通用的良好選擇:

  • 所有接觸到對(duì)象池代碼的開發(fā)者必須了解對(duì)象池,而且能正確處理
  • 哪些代碼知道對(duì)象池,哪些代碼不知道對(duì)象池,其界限必須讓大家知道,并且寫在文檔中
  • 這些額外的復(fù)雜性要保持更新,而且定期復(fù)審
  • 如果有一條不滿足,悄然出現(xiàn)問題(類似于C 中的指針復(fù)用)的風(fēng)險(xiǎn)就又回來了

總之,只有GC停頓不能接受,而且調(diào)校和重構(gòu)也未能將停頓減小到可以接受的水平時(shí),才能使用對(duì)象池。

8.在垃圾收集中,相對(duì)于Parallel Old,CMS總是更好的選擇

Oracle JDK默認(rèn)使用一個(gè)并行的Stop-The-World收集器來收集老年代,即Parallel Old收集器。

Concurrent-Mark-Sweep (CMS)是一個(gè)備選方案,在大部分垃圾收集周期,它允許應(yīng)用線程繼續(xù)運(yùn)行,但這是有代價(jià)的,而且有一些注意事項(xiàng)。

允許應(yīng)用線程與垃圾收集線程一起運(yùn)行,不可避免地帶來一個(gè)問題:應(yīng)用線程修改了對(duì)象圖,可能會(huì)影響對(duì)象的存活性。這種情況必須在事后加以清理,因此CMS實(shí)際上有兩個(gè)STW階段(通常非常短)。

這會(huì)帶來一些后果:

  1. 必須將所有應(yīng)用線程帶到安全點(diǎn),每次Full GC期間會(huì)停頓兩次;
  2. 盡管垃圾收集與應(yīng)用同時(shí)執(zhí)行,但應(yīng)用的吞吐量會(huì)降低(通常是50%);
  3. 在使用CMS進(jìn)行垃圾收集時(shí),JVM所用的簿記信息(和CPU周期)遠(yuǎn)高于其他的并行收集器。

這些代價(jià)是不是物有所值,取決于應(yīng)用的情況。但是天下沒有免費(fèi)的午餐。CMS收集器在設(shè)計(jì)上值得稱道,但它不是萬能的。

所以在確定CMS是正確的垃圾收集策略之前,首先應(yīng)該確認(rèn)Parallel Old的STW停頓確實(shí)不能接受,而且已經(jīng)無法調(diào)校。最后,我重點(diǎn)強(qiáng)調(diào)一下,所有指標(biāo)必須從與生產(chǎn)系統(tǒng)等價(jià)的系統(tǒng)中獲得。

9.增加堆的大小可以解決內(nèi)存問題

當(dāng)應(yīng)用陷入困境,并且懷疑是GC的問題時(shí),很多應(yīng)用團(tuán)隊(duì)的反應(yīng)就是增加堆的大小。在某些情況下,這樣做可以快速見效,而且為我們留出了時(shí)間來考慮更周詳?shù)慕鉀Q方案。然而,如果沒有充分理解性能問題的原因,這種策略反而會(huì)讓事情變得更糟糕。

考慮一個(gè)編碼非常糟糕的應(yīng)用程序,它正在產(chǎn)生很多領(lǐng)域?qū)ο?(它們的生存時(shí)間很有代表性,比如說是2-3秒)。如果分配率高到一定程度,垃圾收集會(huì)頻繁進(jìn)行,這樣領(lǐng)域?qū)ο髸?huì)被提升到老年代。領(lǐng)域?qū)ο髱缀跏且贿M(jìn)入年老代,生存時(shí)間就結(jié)束了,從而直接死亡,但它們直到下一次Full GC時(shí)才會(huì)被回收。

如果增加了應(yīng)用的堆大小,我們所做的不過是增加了相對(duì)短命的對(duì)象進(jìn)入和死亡所用的空間。這會(huì)導(dǎo)致Stop-The-World停頓時(shí)間更長,對(duì)應(yīng)用并無益處。

在修改堆大小或者調(diào)校其他參數(shù)之前,理解對(duì)象的分配和生存時(shí)間的動(dòng)態(tài)是很有必要的。沒有測量性能數(shù)據(jù)就盲目行動(dòng),只會(huì)使情況更糟糕。在這里,垃圾收集器的老年代分布情況特別重要。

結(jié)論

當(dāng)談到Java的性能調(diào)校時(shí),直覺常常起誤導(dǎo)作用。我們需要實(shí)驗(yàn)數(shù)據(jù)和工具來幫助我們將平臺(tái)的行為可視化并加強(qiáng)理解。

垃圾收集就是最好的例子。對(duì)于調(diào);蛘呱芍笇(dǎo)調(diào)校的數(shù)據(jù)而言,GC子系統(tǒng)擁有無限的潛力;但是對(duì)于產(chǎn)品應(yīng)用而言,不使用工具很難理解所產(chǎn)生數(shù)據(jù)的意義。

默認(rèn)情況下,運(yùn)行任意Java進(jìn)程(包括開發(fā)環(huán)境和產(chǎn)品環(huán)境),應(yīng)該至少總是使用如下參數(shù):

-verbose:gc(打印GC日志)
-Xloggc:(更全面的GC日志)
-XX:+PrintGCDetails(更詳細(xì)的輸出)
-XX:+PrintTenuringDistribution(顯示JVM所使用的將對(duì)象提升進(jìn)入老年代的年齡閾值)

然后使用工具來分析日志,這里可以利用手寫的腳本,可以用圖生成,還可以使用GCViewer(開源的)或jClarity Censum這樣的可視化工具。

標(biāo)簽: Google 安全 代碼 服務(wù)器 服務(wù)器硬件 互聯(lián)網(wǎng) 腳本 開發(fā)者 媒體 評(píng)測 數(shù)據(jù)庫

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:50條大牛C++編程開發(fā)學(xué)習(xí)建議

下一篇:編程面試過程中常見的10大算法概念匯總