国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java HotSpot VM中的JIT編譯

caozhijian / 2712人閱讀

摘要:如同其它虛擬機(jī),虛擬機(jī)為字節(jié)碼提供了一個(gè)運(yùn)行時(shí)環(huán)境。編譯是一個(gè)混合模式的虛擬機(jī),也就是說它既可以解釋字節(jié)碼,又可以將代碼編譯為本地機(jī)器碼以更快的執(zhí)行。解決此問題一般是在進(jìn)程啟動(dòng)后,對(duì)代碼進(jìn)行預(yù)熱以使它們被強(qiáng)制編譯。

Java HotSpot虛擬機(jī)是Oracle收購Sun時(shí)獲得的,JVM和開源的OpenJDK都是以此虛擬機(jī)為基礎(chǔ)發(fā)展的。如同其它虛擬機(jī),HotSpot虛擬機(jī)為字節(jié)碼提供了一個(gè)運(yùn)行時(shí)環(huán)境。實(shí)際上,它主要會(huì)做這三件事情:

執(zhí)行方法所請(qǐng)求的指令和運(yùn)算。

定位、加載和驗(yàn)證新的類型(即類加載)。

管理應(yīng)用內(nèi)存。

最后兩點(diǎn)都是各自領(lǐng)域的大話題,所以這篇文章中只關(guān)注代碼執(zhí)行。

JIT編譯

Java HotSpot是一個(gè)混合模式的虛擬機(jī),也就是說它既可以解釋字節(jié)碼,又可以將代碼編譯為本地機(jī)器碼以更快的執(zhí)行。通過配置-XX:+PrintCompilation參數(shù),你可以在log文件中看到方法被JIT編譯時(shí)的信息。JIT編譯發(fā)生在運(yùn)行時(shí) —— 方法經(jīng)過多次運(yùn)行之后。到方法需要使用到的時(shí)候,HotSpot VM會(huì)決定如何優(yōu)化這些代碼。

如果你好奇JIT編譯帶來的性能提升,可以使用-Djava.compiler=none將其關(guān)掉然后運(yùn)行基準(zhǔn)測試程序來看看它們的差別。

Java HotSpot虛擬機(jī)可以運(yùn)行在兩種模式下:client或者server。你可以在JVM啟動(dòng)時(shí)通過配置-client或者-server選項(xiàng)來選擇其中一種。兩種模式都有各自的適用場景,本文中,我們只會(huì)涉及到server模式。

兩種模式最主要的區(qū)別是server模式下會(huì)進(jìn)行更激進(jìn)的優(yōu)化 —— 這些優(yōu)化是建立在一些并不永遠(yuǎn)為真的假設(shè)之上。一個(gè)簡單的保護(hù)條件(guard condition)會(huì)驗(yàn)證這些假設(shè)是否成立,以確保優(yōu)化總是正確的。如果假設(shè)不成立,Java HotSpot虛擬機(jī)將會(huì)撤銷所做的優(yōu)化并退回到解釋模式。也就是說Java HotSpot虛擬機(jī)總是會(huì)先檢查優(yōu)化是否仍然有效,不會(huì)因?yàn)榧僭O(shè)不再成立而表現(xiàn)出錯(cuò)誤的行為。

在server模式下,Java
HotSpot虛擬機(jī)會(huì)默認(rèn)在解釋模式下運(yùn)行方法10000次才會(huì)觸發(fā)JIT編譯。可以通過虛擬機(jī)參數(shù)-XX:CompileThreshold來調(diào)整這個(gè)值。比如-XX:CompileThreshold=5000會(huì)讓觸發(fā)JIT編譯的方法運(yùn)行次數(shù)減少一半。(譯者注:有關(guān)JIT觸發(fā)條件可參考《深入理解Java虛擬機(jī)》第十一章以及《Java Performance》第三章HotSpot VM JIT Compilers小節(jié))

這可能會(huì)誘使新手將編譯閾值調(diào)整到一個(gè)非常低的值。但要抵擋住這個(gè)誘惑,因?yàn)檫@樣可能會(huì)降低虛擬機(jī)性能,優(yōu)化后減少的方法執(zhí)行時(shí)間還不足以抵消花在JIT編譯上的時(shí)間。

當(dāng)Java HotSpot虛擬機(jī)能為JIT編譯收集到足夠多的統(tǒng)計(jì)信息時(shí),性能會(huì)最好。當(dāng)你降低編譯閾值時(shí),Java HotSpot虛擬機(jī)可能會(huì)在非熱點(diǎn)代碼的編譯中花費(fèi)較多時(shí)間。有些優(yōu)化只有在收集到足夠多的統(tǒng)計(jì)信息時(shí)才會(huì)進(jìn)行,所以降低編譯閾值可能導(dǎo)致優(yōu)化效果不佳。

另外一方面,很多開發(fā)者想讓一些重要方法在編譯模式下盡快獲得更好的性能。

解決此問題一般是在進(jìn)程啟動(dòng)后,對(duì)代碼進(jìn)行預(yù)熱以使它們被強(qiáng)制編譯。對(duì)于像訂單系統(tǒng)或者交易系統(tǒng)來說,重要的是要確保預(yù)熱不會(huì)產(chǎn)生真實(shí)的訂單。

Java HotSpot虛擬機(jī)提供了很多參數(shù)來輸出JIT的編譯信息。最常用的就是前文提到的PrintCompilation,也還有一些其它參數(shù)。

接下來我們將使用PrintCompilation來觀察Java HotSpot虛擬機(jī)在運(yùn)行時(shí)編譯方法的成效。但先有必要說一下用于計(jì)時(shí)的System.nanoTime()方法。

計(jì)時(shí)方法

Java為我們提供了兩個(gè)主要的獲取時(shí)間值的方法:currentTimeMillis()和nanoTime().前者對(duì)應(yīng)于我們?cè)趯?shí)體世界中看到的時(shí)間(所謂的鐘表時(shí)間),它的精度能滿足大多數(shù)情況,但不適用于低延遲的應(yīng)用。

納秒計(jì)時(shí)器擁有更高的精度。這種計(jì)時(shí)器度量時(shí)間的間隔極短。1納秒是光在光纖中移動(dòng)20CM所需的時(shí)間,相比之下,光通過光纖從倫敦傳送到紐約大約需要27.5毫秒。

因?yàn)榧{秒級(jí)的時(shí)間戳精度太高,使用不當(dāng)就會(huì)產(chǎn)生較大誤差,因此使用時(shí)需要注意。

如,currentTimeMillis()能很好的在機(jī)器間同步,可以用于測量網(wǎng)絡(luò)延遲,但nanoTime()不能跨機(jī)器使用。

接下來將上面的理論付諸實(shí)踐,來看一個(gè)很簡單(但極其強(qiáng)大)的JIT編譯技術(shù)。

方法內(nèi)聯(lián)

方法內(nèi)聯(lián)是編譯器優(yōu)化的關(guān)鍵手段之一。方法內(nèi)聯(lián)就是把方法的代碼“復(fù)制”到發(fā)起調(diào)用的方法里,以消除方法調(diào)用。這個(gè)功能相當(dāng)重要,因?yàn)檎{(diào)用一個(gè)小方法可能比執(zhí)行該小方法的方法體耗時(shí)還多。

JIT編譯器可以進(jìn)行漸進(jìn)內(nèi)聯(lián),開始時(shí)內(nèi)聯(lián)簡單的方法,如果可以進(jìn)行其它優(yōu)化時(shí),就接著優(yōu)化內(nèi)聯(lián)后的較大的代碼塊。

Listing1,Listing1A以及Listing1B是個(gè)簡單的測試,將直接操作字段和通過getter/setter方法做了對(duì)比。如果簡單的getters和setters方法沒有使用內(nèi)聯(lián)的話,那調(diào)用它們的代價(jià)是相當(dāng)大的,因?yàn)榉椒ㄕ{(diào)用比直接操作字段代價(jià)更高。

Listing1:

public class Main {
    private static double timeTestRun(String desc, int runs, 
        Callable callable) throws Exception {
        long start = System.nanoTime();
        callable.call();
        long time = System.nanoTime() - start;
        return (double) time / runs;
    }

    // Housekeeping method to provide nice uptime values for us
    private static long uptime() {
        return ManagementFactory.getRuntimeMXBean().getUptime() + 15; 
    // fudge factor
    }

    public static void main(String... args) throws Exception {
        int iterations = 0;
        for (int i : new int[]
            { 100, 1000, 5000, 9000, 10000, 11000, 13000, 20000, 100000} ) {
            final int runs = i - iterations;
            iterations += runs;

            // NOTE: We return double (sum of values) from our test cases to
            // prevent aggressive JIT compilation from eliminating the loop in
            // unrealistic ways
            Callable directCall = new DFACaller(runs);
            Callable viaGetSet = new GetSetCaller(runs);

            double time1 = timeTestRun("public fields", runs, directCall);
            double time2 = timeTestRun("getter/setter fields", runs, viaGetSet);

            System.out.printf("%7d %,7d		field access=%.1f ns, getter/setter=%.1f ns%n",
                uptime(), iterations, time1, time2);
            // added to improve readability of the output
            Thread.sleep(100);
        }
    }
}

Listing1A:

public class DFACaller implements Callable{
    private final int runs;

    public DFACaller(int runs_) {
        runs = runs_;
    }

    @Override
    public Double call() {
        DirectFieldAccess direct = new DirectFieldAccess();
        double sum = 0;
        for (int i = 0; i < runs; i++) {
            direct.one++;
            sum += direct.one;
        }
        return sum;
    }
}

public class DirectFieldAccess {
    int one;
}

Listing1B:

public class GetSetCaller implements Callable {
    private final int runs;

    public GetSetCaller(int runs_) {
        runs = runs_;
    }

    @Override
    public Double call() {
        ViaGetSet getSet = new ViaGetSet();
        double sum = 0;
        for (int i = 0; i < runs; i++) {
            getSet.setOne(getSet.getOne() + 1);
            sum += getSet.getOne();
        }
        return sum;
    }
}

public class ViaGetSet {
    private int one;

    public int getOne() {
        return one;
    }

    public void setOne(int one) {
        this.one = one;
    }
}

如果使用java -cp. -XX:PrintCompilation Main 運(yùn)行測試用例,就能看到性能上的差異(見Listing2)。

Listing2

 31    1     java.lang.String::hashCode (67 bytes) 
 36   100    field access=1970.0 ns, getter/setter=1790.0 ns 
 39    2     sun.nio.cs.UTF_8$Encoder::encode (361 bytes) 
 42    3     java.lang.String::indexOf (87 bytes) 
141   1,000 field access=16.7 ns, getter/setter=67.8 ns 
245   5,000 field access=16.8 ns, getter/setter=72.8 ns 
245    4     ViaGetSet::getOne (5 bytes) 
348   9,000 field access=16.0 ns, getter/setter=65.3 ns 
450    5     ViaGetSet::setOne (6 bytes) 
450  10,000 field access=16.0 ns, getter/setter=199.0 ns 
553    6     Main$1::call (51 bytes) 
554    7     Main$2::call (51 bytes) 
556    8     java.lang.String::charAt (33 bytes) 
556  11,000 field access=1263.0 ns, getter/setter=1253.0 ns 
658  13,000 field access=5.5 ns, getter/setter=1.5 ns 
760  20,000 field access=0.7 ns, getter/setter=0.7 ns 
862 100,000 field access=0.7 ns, getter/setter=0.7 ns 

這些是什么意思?Listing2中的第一列是程序啟動(dòng)到語句執(zhí)行時(shí)所經(jīng)過的毫秒數(shù),第二列是方法ID(編譯后的方法)或遍歷次數(shù)。

注意:測試中沒有直接使用String和UTF_8類,但它們?nèi)匀怀霈F(xiàn)在編譯的輸出中,這是因?yàn)槠脚_(tái)使用了它們。

從Listing2中的第二行可以發(fā)現(xiàn),直接訪問字段和通過getter/setter都是比較慢的,這是因?yàn)榈谝淮芜\(yùn)行時(shí)包含了類加載的時(shí)間,下一行就比較快了,盡管此時(shí)還沒有任何代碼被編譯。

另外要注意下面幾點(diǎn):

在遍歷1000和5000次時(shí),直接操作字段比使用getter/setter方法快,因?yàn)間etter 和setter還沒有內(nèi)聯(lián)或優(yōu)化。即便如此,它們都還相當(dāng)?shù)乜臁?/p>

在遍歷9000次時(shí),getter方法被優(yōu)化了(因?yàn)槊看窝h(huán)中調(diào)用了兩次),使性能有小許提高。

在遍歷10000次時(shí),setter方法也被優(yōu)化了,因?yàn)樾枰~外花費(fèi)時(shí)間去優(yōu)化,所以執(zhí)行速度降下來了。

最終,兩個(gè)測試類都被優(yōu)化了:

DFACaller直接操作字段,GetSetCaller使用getter和setter方法。此時(shí)它們不僅剛被優(yōu)化,還被內(nèi)聯(lián)了。

從下一次的遍歷中可以看到,測試用例的執(zhí)行時(shí)間仍不是最快的。

在13000次遍歷之后,兩種字段訪問方式的性能都和最后更長時(shí)間測試的結(jié)果一樣好,我們已經(jīng)達(dá)到了性能的穩(wěn)定狀態(tài)。

需要特別注意的是,直接訪問字段和通過getter/setter訪問在穩(wěn)定狀態(tài)下的性能是基本一致的,因?yàn)榉椒ㄒ呀?jīng)被內(nèi)聯(lián)到GetSetCaller中,也就是說在viaGetSet中所做的事情和directCall中完全一樣。

JIT編譯是在后臺(tái)進(jìn)行的。每次可用的優(yōu)化手段可能隨機(jī)器的不同而不同,甚至,同個(gè)程序的多次運(yùn)行期間也可能不一樣。

總結(jié)

這篇文章中,我所描述的只是JIT編譯的冰山一角,尤其是沒有提到如何寫出好的基準(zhǔn)測試以及如何使用統(tǒng)計(jì)信息以確保不會(huì)被平臺(tái)的動(dòng)態(tài)性所愚弄。

這里使用的基準(zhǔn)測試非常簡單,不適合做為真實(shí)的基準(zhǔn)測試。在第二部分,我計(jì)劃向您展示一個(gè)真實(shí)的基準(zhǔn)測試并繼續(xù)深入JIT編譯的過程。

原文 Introduction to JIT Compliation in Java Hotspot VM
譯者 郭蕾
校對(duì)丁一
via ifeve

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/64069.html

相關(guān)文章

  • 學(xué)習(xí)JVM必看書籍

    學(xué)習(xí)JVM的相關(guān)資料 《深入理解Java虛擬機(jī)——JVM高級(jí)特性與最佳實(shí)踐(第2版)》 showImg(https://segmentfault.com/img/bVbsqF5?w=200&h=200); 基于最新JDK1.7,圍繞內(nèi)存管理、執(zhí)行子系統(tǒng)、程序編譯與優(yōu)化、高效并發(fā)等核心主題對(duì)JVM進(jìn)行全面而深入的分析,深刻揭示JVM的工作原理。以實(shí)踐為導(dǎo)向,通過大量與實(shí)際生產(chǎn)環(huán)境相結(jié)合的案例展示了解...

    shaonbean 評(píng)論0 收藏0
  • JAVA刷題(持續(xù)更新)

    摘要:年期間微軟公司發(fā)布,無論是技術(shù)實(shí)現(xiàn)上還是目標(biāo)用戶都與有很多相近之處,給帶來了很多討論比較和競爭對(duì)的發(fā)展進(jìn)程影響很大。年月日大會(huì)上,公司宣布最終會(huì)將開源。及其他與公司爭奪的控制權(quán),令從跨平臺(tái)技術(shù)變?yōu)榻壎ㄔ谏系募夹g(shù)是微軟公司的主要目的。 一、基礎(chǔ) 面向?qū)ο?面向?qū)ο蟾拍?面向?qū)ο?Object Oriented,OO)是軟件開發(fā)方法 對(duì)象:萬物皆對(duì)象,對(duì)象可以是具體的事物,也可以是抽象...

    cyixlq 評(píng)論0 收藏0
  • 譯文-Hotspot VM中Oop Maps的含義?

    摘要:原文出處是記錄棧中引用對(duì)象的數(shù)據(jù)結(jié)構(gòu)。它的主要作用是發(fā)現(xiàn)棧中的對(duì)象,當(dāng)對(duì)象被移動(dòng)到堆中更新該對(duì)象的應(yīng)用。采用延遲計(jì)算算法,當(dāng)發(fā)生時(shí),通過字節(jié)碼流分析。開發(fā)者手動(dòng)創(chuàng)建這些運(yùn)行時(shí)副本的作者。 原文出處:What does Oop Maps means in Hotspot VM exactly Oop Maps是記錄Java棧中引用對(duì)象的數(shù)據(jù)結(jié)構(gòu)。它的主要作用是發(fā)現(xiàn)Java棧中的GC Ro...

    wujl596 評(píng)論0 收藏0
  • 實(shí)戰(zhàn)Java虛擬機(jī)之二“虛擬機(jī)的工作模式”

    摘要:今天開始實(shí)戰(zhàn)虛擬機(jī)之二虛擬機(jī)的工作模式。總計(jì)有個(gè)系列實(shí)戰(zhàn)虛擬機(jī)之一堆溢出處理實(shí)戰(zhàn)虛擬機(jī)之二虛擬機(jī)的工作模式實(shí)戰(zhàn)虛擬機(jī)之三的新生代實(shí)戰(zhàn)虛擬機(jī)之四禁用實(shí)戰(zhàn)虛擬機(jī)之五開啟編譯目前的虛擬機(jī)支持和兩種運(yùn)行模式。 今天開始實(shí)戰(zhàn)Java虛擬機(jī)之二:虛擬機(jī)的工作模式。 總計(jì)有5個(gè)系列實(shí)戰(zhàn)Java虛擬機(jī)之一堆溢出處理實(shí)戰(zhàn)Java虛擬機(jī)之二虛擬機(jī)的工作模式實(shí)戰(zhàn)Java虛擬機(jī)之三G1的新生代GC實(shí)戰(zhàn)Jav...

    focusj 評(píng)論0 收藏0
  • JAVA運(yùn)行時(shí)簡述(HotSpot)

    摘要:拆解虛擬機(jī)的基本步聚如下首先,要等待到自身成為唯一一個(gè)正在運(yùn)行的非守護(hù)線程時(shí),在整個(gè)等待過程中,虛擬機(jī)仍舊是可工作的。將相應(yīng)的事件發(fā)送給,禁用,并終止信號(hào)線程。 本文簡單介紹HotSpot虛擬機(jī)運(yùn)行時(shí)子系統(tǒng),內(nèi)容來自不同的版本,因此可能會(huì)與最新版本之間(當(dāng)前為JDK12)存在一些誤差。 1.命令行參數(shù)處理HotSpot虛擬機(jī)中有大量的可影響性能的命令行屬性,可根據(jù)他們的消費(fèi)者進(jìn)行簡...

    hosition 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<