2016-03-24 53 views
30

我知道在Java虛擬機(JVM)中,當Java使用延遲加載過程加載類時可能需要預熱,因此您需要確保在啓動主事務之前初始化對象。我是一名C++開發人員,無需處理類似的需求。爲什麼JVM需要預熱?

不過,我無法理解的部分如下:

  1. 的部分代碼,你應該溫暖,向上?
  2. 即使我對代碼的某些部分進行了預熱,它仍然保持多久(假設這個術語僅表示類對象保留在內存中的時間)?
  3. 如果我每次收到事件時都需要創建對象,它有什麼用處?

考慮一個預期通過套接字接收消息並且交易可以是新訂單,修改訂單和取消訂單或確認交易的應用程序示例。

請注意,該應用程序是關於高頻交易(HFT),因此性能非常重要。

+2

您可能會發現用於跟蹤編譯行爲的'-XX:+ PrintCompilation'。您也可能想聯繫oracle,他們正在開發AOT編譯器,目前僅向商業客戶提供AIUI。我認爲其他一些JVM供應商也提供AOT。 – the8472

+1

另請參閱以下鏈接:[Windows上的jvm選項](http://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html),[Linux,Solaris,Mac上的jvm選項] (http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html) –

回答

22

你應該預熱哪些部分的代碼?

通常,你不必做任何事情。但是對於低延遲應用,您應該預熱系統中的關鍵路徑。你應該有單元測試,所以我建議你在啓動時運行這些代碼來加熱代碼。

即使您的代碼被預熱,也必須確保CPU緩存保持溫暖。在阻止操作之後,您可以看到顯着的性能下降,例如,網絡IO,長達50微秒。通常情況下這不是問題,但是如果你在大多數情況下試圖保持在50微秒以下,這在大多數情況下都會成爲問題。

注意:預熱可以允許Escape Analysis踢入並將一些物體放置在堆棧上。這意味着這些對象不需要進行優化。在優化代碼之前,最好記住您的應用程序的配置文件。

即使我對代碼的某些部分進行了預熱,它會保持多長時間(假設這個術語僅表示類對象保留在內存中的時間)?

沒有時間限制。這取決於JIt是否檢測到優化代碼時所做的假設是否不正確。

如果我每次收到事件時都需要創建對象,它有什麼用處?

如果您想要低延遲或高性能,您應該創建儘可能少的對象。我的目標是生產速度低於300 KB /秒。有了這個分配率,您可以擁有足夠大的伊甸園空間,每天輕微收集一次。

考慮一個應用程序的例子,期望通過套接字接收消息,並且事務可以是新訂單,修改訂單和取消訂單或確認的交易。

我建議你儘可能重複使用對象,但如果它在你的分配預算之下,它可能不值得擔心。

請注意,該應用程序是關於高頻交易(HFT),因此性能非常重要。

您可能對我們的開源軟件感興趣,該軟件用於不同投資銀行和對衝基金的HFT系統。

http://chronicle.software/

我的生產應用程序用於高頻交易和延時的每一位可能是一個問題。這很明顯,在啓動時如果你沒有對應用程序進行熱身,它會導致幾毫秒的高延遲。

特別是您可能對https://github.com/OpenHFT/Java-Thread-Affinity感興趣,因爲此庫可以幫助減少關鍵線程中的調度抖動。

而且據說需要預熱的代碼的關鍵部分應該以最少12K次的時間運行(使用假消息),以便以最佳方式工作。爲什麼以及它如何工作?

代碼是使用後臺線程編譯的。這意味着即使某個方法可能符合編譯爲本地代碼的條件,這並不意味着它在編譯器已經非常繁忙時啓動時尤其如此。 12K並非不合理,但可能會更高。

+0

感謝@PeterLawrey的詳細解釋。我只想提出另一個問題作爲後續問題:是否可以記錄或監視代碼的哪一部分被預熱。 – Suparna

+0

@Suparna關鍵路徑的每一部分都應該加熱,包括TCP連接,尤其是那些不經常被調用的連接。我建議Chronicle Queue用於低延遲日誌記錄和持續消息傳遞。 –

+0

@PeterLawrey先生我記得在你的一個視頻中你有一些解決方案(可能是你開發的),這是幫助熱身,我記得像它會保存配置文件,並在下次啓動jvm會預編譯一些方法使用以前的配置文件,因此不等待10萬處決......我可能完全不在,因爲這很久以前... 你有沒有什麼jvm熱身? – vach

2

它是所有關於JIT編譯器,它被用在JVM在運行時優化的字節碼(因爲javac不能使用先進或侵略性優化工藝由於字節代碼的平臺無關性)

  1. 你可以預熱處理你的消息的代碼。實際上,在大多數情況下,你不需要通過特殊的預熱週期去做:只要讓應用程序啓動並處理一些第一條消息 - JVM將盡力分析代碼執行並進行優化:)手動熱身假冒樣品可以產生更糟糕的結果

  2. 代碼將一定量的時間之後進行優化,直到程序流會發生降解碼狀態的一些事件將得到優化(後JIT編譯器會嘗試再次優化代碼 - 這個過程永遠不會結束)

  3. 短生命的對象也是要優化的主題,但通常它應該有助於您的消息處理終身代碼更高效

+0

我遇到了這個JVM標誌-xx:CompileThreshold默認設置爲10000.這是否有任何與你在#1中提到的有關。 – Suparna

+0

而且據說需要預熱的代碼的關鍵部分應該以最少12K次的時間(以假消息)運行,以便以最佳方式工作。爲什麼以及它如何工作? – Suparna

+1

如果您不確定您在做什麼,不建議您更改默認JIT選項 - Sun和Oracle團隊擁有豐富的專業知識和代碼基礎,可以憑經驗找到通常具有良好效益的值。當然,您可以降低CompileThreshold值,但存在一些缺點 - 內存消耗較大,例如 – Cootri

9

熱身很少要求。舉例來說,性能測試是相關的,以確保JIT預熱時間不會影響結果。

在正常的生產代碼中,您很少看到用於熱身的代碼。 JIT在正常的處理過程中會變暖,所以僅僅爲此引入額外的代碼幾乎沒有什麼優勢。在最糟糕的情況下,您可能會引入錯誤,花費額外的開發時間甚至損害性能。

除非您確定需要進行某種熱身,否則請不要擔心。您描述的示例應用程序當然不需要它。

+1

這可能不是真的。我的生產應用程序用於高頻交易,每一點延遲都可能成爲問題。這很明顯,在啓動時如果你沒有對應用程序進行熱身,它會導致幾毫秒的高延遲。一旦升級並且JVM優化之後,代碼將提供合適的性能級別。我有興趣瞭解爲什麼以及如何? – Suparna

+3

@Suparna如果您使用Java編寫HFT代碼,您幾乎可以肯定地使用自定義技術(如Chronicle)手動管理資源,並且不適用任何標準JVM建議。 – chrylis

+11

如果你正在處理HFT,那麼你應該在你的問題中說。這是一個完全不同的野獸,普通的Java規則不一定適用。您可能需要查看[OpenHFT](https://github.com/OpenHFT/Java-Lang)以獲取更多信息(這是StackOverflow常旅客Peter Lawrey先生的工作)。 – Kayaman

18

變暖指的是讓一段代碼運行足夠多的時間,JVM停止解釋並編譯爲本機(至少是第一次)。通常這是你不想做的事情。原因是JVM收集代碼生成期間使用的有關代碼的統計信息(類似於配置文件指導的優化)。因此,如果有問題的代碼塊被虛假數據「加熱」,而虛擬數據具有與真實數據不同的屬性,那麼您可能會損害性能。

編輯:由於JVM不能執行整個程序的靜態分析(它無法知道應用程序將加載哪些代碼),它可以改爲從已收集的統計數據中對類型進行一些猜測。作爲一個例子,當在一個確切的調用位置調用一個虛函數(用C++講話)並且它確定所有類型具有相同的實現時,則該調用被提升爲直接調用(或甚至內聯)。如果後來假設如果被證明是錯誤的,那麼舊代碼必須「未編譯」才能正常運行。 AFAIK HotSpot將呼叫站點分爲單態(單一實現),雙態(正好兩個)。轉換爲if(imp1-type){imp1} else {imp2})和完全多態虛擬調度。

還有另一種情況,即重新編譯時出現..當你有分層編譯。第一層將花費更少的時間試圖產生良好的代碼,如果該方法是「熱就好」,然後在更昂貴的編譯時間碼發生器踢。

+0

我猜測次數可能至少是12K次。一旦JVM將代碼編譯爲本地代碼,是否確保在剩餘的處理時間內保持此狀態? – Suparna

+1

@Suparna如果JIT認爲有必要,可以自由地「解編」(並重新編譯)代碼。 – Kayaman

+0

@Kayaman - 開發過程中是否有任何技術/方法可以延長JIT不會編譯/重新編譯代碼的時間? – Suparna

2

的部分代碼,你應該熱身哪個?

這個問題一般沒有答案。這完全取決於您的應用程序。

即使我熱身的代碼的某些部分,如何長時間保持溫暖 (僅假設這個詞意味着你的類的對象多長時間留在記憶 )?

只要你的程序有一個對它們的引用,缺少任何特殊的弱引用用法或類似的東西,對象就會留在內存中。瞭解你的程序什麼時候「具有對某事的引用」可能會比你初看時更加模糊,但它是Java內存管理的基礎,值得付出努力。

如果我每次都需要創建對象,它會有什麼幫助 我收到一個事件。

這完全取決於應用。一般來說沒有答案。

我鼓勵您學習和使用Java來理解類加載,內存管理和性能監視等事情。它需要一些時間來實例化一個對象,一般來說,它需要更多的時間來加載一個類(當然,這通常要少得多)。 通常,一旦加載了一個類,它就會保留在程序生命的內存中 - 這就是你應該理解的,不只是得到答案。

如果你不知道它們,也有技巧可以學習。某些程序使用對象的「池」,在實際需要之前將其實例化,然後在需要出現時移交給對象進行處理。這允許程序的時間關鍵部分避免在時間關鍵期間花費實例化的時間。這些池維護一組對象(10?100?1000?10000?),並在需要時實例化更多內容等。但是,管理這些池是一項重要的編程工作,當然,您會使用池中的對象佔用內存。

完全有可能使用足夠多的內存來更頻繁地觸發垃圾回收,並減慢您希望加速的系統。這就是爲什麼你需要了解它是如何工作的,而不僅僅是「得到答案」。

另一個考慮因素 - 到目前爲止,大部分加快程序設計的努力都浪費了,因爲不需要。如果沒有正在考慮的應用程序和/或系統測量方面的豐富經驗,您只是不知道哪裏()優化甚至會顯着。系統/程序設計可以避免緩慢的病態情況,因此非常有用,並且不需要花費大量的時間和精力進行「優化」。大多數情況下,這都是我們需要的。

- 編輯 - 將即時編譯添加到要研究和理解的事物列表中。

+0

謝謝你。我會一次把你提到的每一個題目都記下來。 – Suparna

2

爲什麼JVM需要熱身?

現代(J)VM在運行時收集統計信息,瞭解哪些代碼最常用以及如何使用。其中一個(如果不是成千上萬)例子是對虛擬函數調用的優化(用C++術語),它們只有在實現時纔有效。這些統計信息只能在運行時收集。

類加載本身是熱身的一部分爲好,但它顯然是這些類裏面的代碼執行前會自動進行,所以沒有太多的擔心

代碼的哪些部分應該你熱身?

這是你的應用程序的性能是至關重要的組成部分。最重要的部分是「正常地使用它」,正如它在正常使用中使用的方式一樣,否則錯誤的優化將會完成(並在稍後撤消)。

即使我對代碼的某些部分進行了預熱,它會保持多長時間(假設這個術語僅表示類對象保留在內存中的時間)?

這實在很難說基本上JIT編譯器會持續監視執行和性能。如果達到某個閾值,它會嘗試優化。然後它將繼續監視性能,以驗證優化實際上是否有幫助。如果不是,它可能會不優化代碼。還有可能發生的事情,無效的優化,如加載新的類。我會考慮那些事情無法預測,至少不是基於計算器的答案,但有工具告訴你的JIT是這樣做的:https://github.com/AdoptOpenJDK/jitwatch

它是如何幫助,如果我有需要是對象每次收到活動時創建。

一個簡單的例子可能是:您在方法內部創建對象,因爲引用會離開方法的範圍,這些對象將存儲在堆中,並最終被垃圾收集器收集。如果使用這些對象的代碼被大量使用,它可能最終以單個大方法進行內聯,可能會被重新排序以至於無法識別,直到這些對象僅存在於此方法內。在那時,他們可以放在堆棧上並在方法退出時被刪除。這可以節省大量垃圾回收,並且只會在一些熱身之後纔會發生。所有這些說法:我對一個人需要做任何特殊的熱身操作這個概念持懷疑態度。只需啓動你的應用程序,並使用它,JIT編譯器就可以做到這一點。如果您遇到問題,那麼請了解JIT在您的應用程序中所做的工作,以及如何微調該行爲或如何編寫應用程序以使其最有利。

我真正瞭解需要熱身的唯一情況是基準。因爲如果你忽視它,你會得到幾乎保證的虛假結果。

1

我總是描繪它類似如下:

你爲(a C++開發者)可以想像通過jvm編譯/ hotloading一個自動化迭代方法/使用(的虛類似物)替換的各種比特的片gcc -O0-O1-O2-O3變種(有時恢復他們,如果它認爲neccessary)

我敢肯定,這是不嚴格會發生什麼,但可能是一個C++開發一個有用的類比。

在標準jvm上,片段被認爲是jit所需的時間由-XX:CompileThreshold設置,默認爲1500。 (來源和JVM版本有所不同 - 但我認爲jvm8多數民衆贊成)

另外一個book我下主機服務表現JIT章(P59),以下優化是JIT期間完成手頭上的狀態:

  • 內聯
  • 鎖消除
  • 虛擬呼叫消除
  • 非易失性存儲器寫入消除
  • 本地代碼生成關合作

編輯:

有關意見

我覺得1500可能是剛夠提示JIT,它應該編譯 代碼爲本地和停止解釋。你會同意嗎?

我不知道,如果它只是一個提示,但由於OpenJDK的是開放源代碼讓我們看看各種限制和數字globals.hpp#[email protected](用於jdk8u)

(我不是一個JVM開發這可能是完全地錯誤的地方尋找)

編譯代碼到本機並不一定意味着它也是 優化。

我的理解 - 真實的;特別是如果你的意思是-Xcomp(強制編譯) - 這個blog甚至說,如果你不運行-Xmixed(默認),它會阻止jvm執行任何分析 - 因此進行優化。

因此,一個計時器開始採樣頻繁訪問本機代碼和 優化相同。你知道我們如何控制這個定時器間隔嗎?

我真的不知道細節,但我鏈接的gobals.hpp確實定義了一些頻率間隔。

+0

謝謝。這是一個簡單而有用的比喻。在這方面我有兩個問題。 1.我認爲1500可能足以暗示JIT應該將代碼編譯爲本機並停止解釋。你會同意嗎? 2.將代碼編譯爲本地代碼並不一定意味着它也被優化。因此,計時器開始對經常訪問的本地代碼進行採樣並對其進行優化。你知道我們如何控制這個定時器間隔嗎? – Suparna