JVM Code Cache空間不足,導致服務性能變慢

有業務反饋,線上一個應用運行了一段時間之后,在高峰期之后,突然發現處理能力下降,接口的響應時間變長,但是看Cat上的GC數據,一切都很正常。

通過跳板機上機器查看日志,發現一段平時很少見到的日志:

Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=.
...
“CompilerThread0” java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?

其中CodeCache is full,說明Code Cache已經滿了,導致Compiler失效,這是為什么?

首先,我們得了解什么是Code Cache。

什么是Code Cache

Java代碼在執行次數達到一個閾值會觸發JIT編譯,一旦代碼塊被編譯成本地機器碼,下次執行的時候會直接運行編譯后的本地機器碼。所以這本地機器碼必須被緩存起來,而緩存這個本地機器碼的內存區域就是Code Cache,它并不屬于Java堆的一部分,除了JIT編譯的代碼之外,Java所使用的本地方法代碼(JNI)也會存在codeCache中。

Code Cache 調優

由于Code Cache是一塊內存區域,那么肯定有大小的限制,但是不同版本的JVM、不同的啟動方式,Code Cache的默認大小也不同,可通過jinfo -flag ReservedCodeCacheSize 進行查看。

服務啟動之后,隨著時間的推移,肯定會有越來越多的方法被JIT編譯成本地機器碼,并存放到Code Cache,由于Code Cache大小是固定的,那么就存在被用完的風險。

一旦Code Cache被填滿,就會出現下面情況:

  • JVM的JIT功能會被停止,將不會編譯任何額外的代碼。
  • 被編譯過的代碼仍然以編譯方式執行,但是尚未被編譯的代碼只能以解釋方式執行了。

這種情況下,如果應用中還有很多代碼以解釋方式執行,其性能會大大降低。為了避免這種情況,就需要對Code Cache比較深入的理解。

JVM啟動的時候,Code Cache所需內存會被單獨初始化,這時候Java堆還會被初始化,所以Code Cache和Java堆是兩塊獨立內存區域。

codeCache.cppCodeCache::initialize()方法中,實現了Code Cache的初始化

Code Cache包含了3種數據:

  • NonNMethodCode
  • ProfiledCode
  • NonProfiledCode

通過SegmentedCodeCache參數可以選擇按照整體初始化,還是分段初始化。

通過-XX:ReservedCodeCacheSize參數可以指定Code Cache的初始化大小,這個默認值在不同的JDK版本也不同,目前我這邊調試的是OpenJDK11,默認大小是240M,這個已經夠用了。

可以看下其它版本的默認大小:

對于那些只有32M、48M的就可能存在Code Cache不足的隱患,增加ReservedCodeCacheSize可以是一個解決方案,但這通常只是一個臨時的解決方案。

幸運的是,JVM提供了一種比較激進的codeCache回收方式:Speculative flushing。

在JDK1.7.0_4之后這種回收方式默認開啟,而之前的版本需要通過一個參數來開啟:-XX:+UseCodeCacheFlushing。

在Speculative flushing開啟的情況下,當Code Cache不足時:

  • 最早被編譯的一半方法將會被放到一個old列表中等待回收;
  • 在一定時間間隔內,如果old列表中方法沒有被調用,這個方法就會被從Code Cache清除;

很不幸的是,在JDK1.7中,Speculative flushing釋放了一部分空間,但是從編譯日志來看,JIT并沒有恢復正常,并且系統整體性能下降很多,出現了大量超時。

在Oracle官網上看到這樣一個Bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8006952 由于算法問題,當Code Cache不足之后會導致編譯線程無法繼續,并且消耗大量CPU,導致系統運行變慢。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 0 問題描述 一個應用在運行一段時間后,隨著訪問量不斷增加,突然處理能力下降。但是從流量,jstack,gc上看基...
    七寸知架構閱讀 3,238評論 1 53
  • 這篇文章是我之前翻閱了不少的書籍以及從網絡上收集的一些資料的整理,因此不免有一些不準確的地方,同時不同JDK版本的...
    高廣超閱讀 15,695評論 3 83
  • 序 本文主要研究一下jvm的Code Cache Code Cache JVM生成的native code存放的內...
    go4it閱讀 16,997評論 0 4
  • Java 應用性能優化是一個老生常談的話題,典型的性能問題如頁面響應慢、接口超時,服務器負載高、并發數低,數據庫頻...
    Rick617閱讀 7,373評論 1 9
  • 紫薇花開(20160824) ――致即將十八的女兒 八月的紫薇花 開了 又謝了 我在窗子里看你 你在風中看太陽 紫...
    張自芳閱讀 739評論 5 9