我們在JBoss上部署了一個大型JDK7應用程序,使用Hibernate,Spring等多個庫。服務器初次啓動後,應用程序按預期運行,但在正常運行一段時間後,它變得非常慢。JDK7應用程序正常運行一段時間後速度變慢
使用探查器,我們已經看到每次外部應用程序的某些方面正在放慢速度,但並不總是相同的方面。雖然在一次運行中可能是hibernate flush變慢,但在另一次運行中它可能是來自Spring的一些DI代碼。
這是怎麼回事?
我們在JBoss上部署了一個大型JDK7應用程序,使用Hibernate,Spring等多個庫。服務器初次啓動後,應用程序按預期運行,但在正常運行一段時間後,它變得非常慢。JDK7應用程序正常運行一段時間後速度變慢
使用探查器,我們已經看到每次外部應用程序的某些方面正在放慢速度,但並不總是相同的方面。雖然在一次運行中可能是hibernate flush變慢,但在另一次運行中它可能是來自Spring的一些DI代碼。
這是怎麼回事?
JDK7中存在一個CodeCache內存區域的問題,這個問題非常非常困難。
說明
基本上Java的啓動和即時編譯(JIT)使用在運行時只編譯字節碼所需的零件。這使JVM能夠在執行期間解除和重新編譯某些代碼片段。如果JVM確定的話,這個發生的事情是,某個代碼片段的初始編譯不是最理想的。 Oracle在JDK 7中引入了名爲分層編譯的功能,該功能允許VM執行此操作。
JVM中的編譯代碼存儲在CodeCache
存儲區中。一直到JDK6默認情況下,這個區域將被填滿,一旦達到100%,JIT將停止編譯,並且會向控制檯輸出錯誤,但是應用程序會像以前一樣運行:已編譯的所有內容都將保持編譯狀態,所有尚未編譯的內容都將在解釋模式下執行(大約慢100倍)
此選項名爲CodeCacheFlushing
,它自JDK7u4起默認啓用。這個想法是,一旦CodeCache
已滿,已編譯代碼的最少使用部分將從內存中刷新,爲其他代碼片段騰出空間。這將使JDK6默認行爲(停止編譯)完全廢棄。它還允許一個更小的CodeCache區域(在JDK7 CodeCache中默認爲48M,如果啓用分層編譯,則爲96M)。
這是錯誤。在JDK7 CodeCache變滿後,JIT停止。接下來是CodeCache區域的刷新。而已。沖洗完成後應重新啓用JIT,但不會發生。此外,控制檯上不會顯示任何警告。更糟糕的是:在禁用JIT之前,大約有一半的已編譯代碼被拋出。
與JDK6相比,JDK6中所有速度都很快並且只能解釋新代碼,而在JDK7中,實際上已經丟失了已編譯和優化的代碼!所有表現良好的應用程序的突然部分將停止執行。應用程序的哪些部分放慢了速度,這使得通過Profiler跟蹤該bug的機率幾乎是不可能的:有時候,用於刷新的休眠代碼在其他時間會減慢,在其他情況下,其代碼爲春季DI代碼或您自己的應用代碼。
受影響嗎?
您可以使用Profiler(JProfiler/YourKit)或JConsole(JVisualVM不會)來監視CodeCache內存區域的內存消耗。通常,CodeCache金額committed
將保持非常接近used
的金額(例如,committed
是23mb,使用的是22mb)。在您的應用程序運行時,committed
和used
直到committed
達到max
。此時used
將急劇下降至max
的1/2 - 2/3。之後,used
不再增長。這就是錯誤會襲擊你的地方。在JConsole中,它看起來就像這樣:
爲什麼是我,而不是所有的人?
很可能你正在使用JBoss。甲骨文很快就發現,有些事情不像他們應該做的那樣,默認情況下禁用了tiered compilation
- 然而紅帽以其無限的智慧來決定,它知道更好,並且重新啓用它。基本上,我們的Web應用程序在Weblogic上運行良好,只有JBoss受到影響,因爲沒有分層編譯(weblogic中未啓用),CodeCache的增長非常小,即使經過數週的運行,我們也從未達到過48mb的閾值。
我該怎麼辦?
首先,決定這個bug是否打到你。其次,讓bug更難以傷害你。如果您禁用CodeCacheFlushing
至少擊中錯誤不會讓事情變得比以前更糟。停止tiered compilation
將使錯誤發生的可能性降低,與增加可用的CodeCache-Memory量相同。
您可以隨時嘗試切換到JDK8,這似乎不受影響,並且您也可以在軟件中實施監視以警告您,如果CodeCache運行已滿。
TL; DR
PRESERVE_JAVA_OPTS=true
-XX:-UseCodeCacheFlushing
)-XX:ReservedCodeCacheSize=xxM
)中。
鏈接到JDK問題:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8012547 –