HotSpot中執(zhí)行引擎技術(shù)詳解(三)——代碼緩存機(jī)制

前兩篇提到宵蕉,HotSpot為了提高執(zhí)行Java字節(jié)碼的速度室抽,采用了一種分階段指令執(zhí)行模型。一種是常見巡扇,但是效率比較低的解釋模式。HotSpot采用了線索解釋和模板解釋器兩項(xiàng)技術(shù)來提高字節(jié)碼指令的執(zhí)行效率垮衷;另外一種厅翔,則是鼎鼎大名的JIT。采用JIT搀突,平均下來每條字節(jié)碼所需的本地機(jī)器指令數(shù)量大為減少刀闷。Java的JIT是建立在局部性原理上的,即一些方法的執(zhí)行會(huì)非常頻繁仰迁,這些方法也被稱為“熱點(diǎn)(HotSpot)”甸昏。這些方法被編譯成本地代碼之后,這些代碼會(huì)被緩存起來徐许,當(dāng)下一次運(yùn)行的時(shí)候就可以直接使用了施蜜,也就是所謂的"code cache"(代碼緩存)。然而雌隅,如同其余緩存翻默,這些代碼緩存也面臨著緩存命中缸沃,緩存失效等問題。


分階段執(zhí)行模式

注:這篇博客不會(huì)討論涉及硬件方面的緩存涉及修械,比如說將緩存的代碼如何分布在L1緩存和泌,在L1上如何執(zhí)行替換等。雖然這方面對(duì)虛擬機(jī)的性能影響也很大祠肥,不過本篇博客只是介紹一下HotSpot在軟件層面上使用的緩存機(jī)制武氓。對(duì)硬件方面有興趣的讀者可以自己去查閱資料,博主也是僅僅了解一點(diǎn)點(diǎn)仇箱。

基礎(chǔ)理論

Method-base

在JVM的JIT和代碼緩存實(shí)現(xiàn)里面县恕,可以分成兩種:

  • 一種是基于方法的代碼JIT編譯與緩存(Method-base)。顧名思義剂桥,就是在判斷是否需要進(jìn)行JIT編譯的時(shí)候忠烛,是以方法作為單位的。比如熱點(diǎn)追蹤里面权逗,追蹤的是方法調(diào)用次數(shù)美尸、頻率等。HotSpot的方法計(jì)數(shù)器就是如此斟薇;
  • 另一種是基于“蹤跡”的代碼JIT編譯與緩存(Trace-base)师坎。這種做法,實(shí)際上是追蹤代碼路徑執(zhí)行的頻率堪滨。舉個(gè)例子來說胯陋,如果一個(gè)條件語句有兩個(gè)分支,那么它會(huì)分別統(tǒng)計(jì)兩個(gè)分支的執(zhí)行次數(shù)袱箱。Android的Dalvik VM遏乔,F(xiàn)irefox里面的TraceMonkeyLua2.0;
void method1(){
    //統(tǒng)計(jì)這一個(gè)方法的執(zhí)行次數(shù)
}

void method2(){
    //統(tǒng)計(jì)該方法執(zhí)行次數(shù)
    // ...其余代碼
    while(someCondition){
        //..統(tǒng)計(jì)這個(gè)循環(huán)的執(zhí)行次數(shù)
    }
    // ...其余代碼
}

這兩種模式发笔,都可以抽象理解為一種按塊編譯的模式盟萨。可以將一個(gè)方法理解為塊了讨,也可以將一個(gè)循環(huán)或者某個(gè)分支理解為一個(gè)塊捻激。因此,JIT或者代碼緩存量蕊,都是以塊為基本單位的铺罢。這里可以看到一個(gè)JIT編譯的塊的大小并不是固定的。因?yàn)榉椒ㄩL度残炮,或者一個(gè)循環(huán)內(nèi)部代碼長度是不可預(yù)測的韭赘。因此在代碼緩存的時(shí)候,不能采用固定大小緩存的方式势就。

代碼緩存置換策略

另外一個(gè)要考慮的是代碼緩存的置換策略泉瞻。緩存空間的大小是有限的脉漏,當(dāng)代碼緩存里面剩余空閑空間不足以容納下一個(gè)JIT產(chǎn)生的本地代碼的時(shí)候,就需要考慮將一些已有的緩存置換出去袖牙,騰出空間以容納新的JIT本地代碼侧巨。這個(gè)管理有點(diǎn)類似于虛擬內(nèi)存的頁管理。置換常用的算法有:

  • 最近最少使用:該算法的最大缺點(diǎn)是需要很多額外的開銷來判斷哪個(gè)是最近最少使用的塊鞭达;
  • 滿時(shí)清除:當(dāng)緩存一滿司忱,就立刻清空,從頭開始畴蹭;
  • 搶先清除:在緩存尚未滿坦仍,即將滿的時(shí)候,就執(zhí)行清空叨襟;
  • FIFO:這個(gè)算法比較大的優(yōu)點(diǎn)是利用了程序的時(shí)間局部性繁扎,因?yàn)樽罱偶尤刖彺娴拇a總是最可能在接下來繼續(xù)被使用,而最先加入緩存的本地代碼糊闽,很可能接下來都不再被使用了梳玫。這個(gè)算法有一個(gè)變種,叫做粗粒度的FIFO右犹。它實(shí)際上是將整個(gè)緩存空間劃分成固定的幾個(gè)塊提澎,如將緩存空間劃分成八塊。以這些粗粒度的塊來作為置換的基本單位傀履;

代碼緩存映射

一個(gè)代碼緩存系統(tǒng)至少要解決一個(gè)問題:給定一個(gè)字節(jié)碼指令虱朵,能夠找到對(duì)應(yīng)的編譯好的代碼緩存的起始PC莉炉,跳轉(zhuǎn)過去執(zhí)行本地代碼钓账。這個(gè)問題通常是使用一個(gè)映射表——一個(gè)維護(hù)了字節(jié)碼到機(jī)器碼的映射關(guān)系的表——來解決的。


字節(jié)碼到機(jī)器碼的映射

但是這僅僅是緩存系統(tǒng)要解決的一個(gè)問題絮宁,還有一個(gè)更加棘手的問題:給定一個(gè)機(jī)器指令梆暮,要找到對(duì)應(yīng)的字節(jié)碼指令。我們現(xiàn)在仔細(xì)考察一下代碼執(zhí)行過程中及其可能出現(xiàn)的問題:拋出異常绍昂。如果整個(gè)代碼都是被解釋執(zhí)行的啦粹,那么問題不大。但是如果在執(zhí)行JIT產(chǎn)生的機(jī)器代碼的過程中窘游,拋出異常了唠椭,怎么辦呢?這就要有一個(gè)回退機(jī)制忍饰,能夠退回到解釋模式下贪嫂,處理異常。一般來說艾蓝,可以額外維護(hù)一個(gè)映射表力崇,該映射表維護(hù)了機(jī)器碼到字節(jié)碼的映射斗塘,每次發(fā)生異常的時(shí)候則查找這個(gè)表來找到對(duì)應(yīng)的字節(jié)碼,于是便會(huì)知道是哪條字節(jié)碼執(zhí)行的時(shí)候出現(xiàn)了異常亮靴。Java的異常機(jī)制要復(fù)雜一點(diǎn)馍盟,因?yàn)楫惓?梢员籧atch住茧吊。所以知道字節(jié)碼的時(shí)候贞岭,需要進(jìn)一步根據(jù)字節(jié)碼PC找到對(duì)應(yīng)的異常處理例程執(zhí)行。

HotSpot里面的實(shí)現(xiàn)

代碼緩存——CodeCache

HotSpot里面對(duì)應(yīng)于代碼緩存的結(jié)構(gòu)搓侄,是CodeCache曹步。CodeCache里面含有極多的靜態(tài)方法,用于管理代碼緩存休讳。最主要的方法有:

// hotspot/src/share/vm/code/codeCache.hpp
static CodeBlob* allocate(int size, bool is_critical = false); // allocates a new CodeBlob
static void free(CodeBlob* cb);                   // frees a CodeBlob

一個(gè)用于分配緩存空間讲婚,一個(gè)用于釋放緩存空間。它們都是在一個(gè)堆上執(zhí)行操作的俊柔。這個(gè)堆就是對(duì)應(yīng)于CodeCache里面的靜態(tài)變量_heap:

  static CodeHeap * _heap;

CodeHeap是一個(gè)堆結(jié)構(gòu)筹麸,其關(guān)鍵性質(zhì)是其內(nèi)維護(hù)了一個(gè)空閑塊的鏈表。注意的是雏婶,這里談?wù)摰膲K和前面談到的JIT編譯的塊是兩個(gè)概念物赶。這里的塊是指內(nèi)存的大小,兩者之間并無嚴(yán)格的對(duì)應(yīng)關(guān)系留晚。

// hotspot/src/share/vm/memory/heap.hpp
class CodeHeap : public CHeapObj<mtCode> {
  //...
  FreeBlock*   _freelist;
  size_t       _freelist_segments;               // No. of segments in freelist
  //...
}

這個(gè)堆的管理方式就如同一般的空閑鏈表支撐的堆的管理方式酵紫。查找空閑塊的時(shí)候,它采用的最佳適應(yīng)错维,這意味著每次都需要整個(gè)空閑鏈表來查找——這是一個(gè)很大的開銷奖地;在釋放空間的時(shí)候,還會(huì)執(zhí)行空閑塊的合并赋焕。這里不贅述参歹,讀者可以去找與數(shù)據(jù)結(jié)構(gòu)、算法相關(guān)的資料來學(xué)習(xí)隆判。

代碼緩存實(shí)體——nmethod

我們先來看前面提到的一個(gè)問題:如何找到對(duì)應(yīng)的JIT編譯產(chǎn)生的機(jī)器代碼犬庇?HotSpot的method-base特性,極大的簡化了這個(gè)問題侨嘀。實(shí)際上它只需要維護(hù)一個(gè)從方法到JIT代碼的映射臭挽。不過在HotSpot里面并沒有使用顯示的一張表來維護(hù)這種關(guān)系,而是在methodOop里面維護(hù)了這種映射:

  //...hotspot/src/share/vm/oops/method.hpp
  // Entry point for calling from compiled code, to compiled code if it exists
  // or else the interpreter.
  volatile address _from_compiled_entry;        // Cache of: _code ? _code->entry_point() : _adapter->c2i_entry()
  // The entry point for calling both from and to compiled code is
  // "_code->entry_point()".  Because of tiered compilation and de-opt, this
  // field can come and go.  It can transition from NULL to not-null at any
  // time (whenever a compile completes).  It can transition from not-null to
  // NULL only at safepoints (because of a de-opt).
  nmethod* volatile _code;
  • _from_compiled_entry: 是執(zhí)行JIT代碼的入口咬腕,與之對(duì)應(yīng)的是_from_interpreted_entry欢峰,它是解釋模式的入口;
  • _code:指向的就是JIT編譯后的代碼。從源碼的注釋里面可以直接看到赤赊,該字段在不為NULL的時(shí)候闯狱,才代表已經(jīng)被JIT編譯了。而且抛计,注釋也表明了哄孤,它可以在NULL和non_null之間轉(zhuǎn)換;

所以在調(diào)用一個(gè)方法的時(shí)候吹截,JVM必然能夠獲得這個(gè)方法的methodOop實(shí)例瘦陈,只需要檢測這個(gè)_code字段,就可以斷定應(yīng)該解釋執(zhí)行波俄,還是應(yīng)該編譯執(zhí)行晨逝。
_code是一個(gè)nmethod的指針。nmethod就是這篇文章要討論一個(gè)核心結(jié)構(gòu)懦铺,它代表了一個(gè)JIT產(chǎn)生的Java方法捉貌。它里面有兩個(gè)核心字段:

  //hotspot/src/share/vm/code/nmethod.hpp
  ExceptionCache *_exception_cache;
  PcDescCache     _pc_desc_cache;

ExceptionCache維護(hù)了異常處理信息,真正的異常處理類實(shí)際上是ExceptionHandlerTable冬念;PcDescCache維護(hù)了PC信息趁窃,它的重要性在于,它維護(hù)了物理機(jī)器PC到字節(jié)碼指令之間的映射急前。還記得在理論部分提出的問題:當(dāng)執(zhí)行本地代碼的時(shí)候醒陆,發(fā)生異常怎么處理?其核心就是利用這兩個(gè)結(jié)構(gòu)裆针。通過PC映射到字節(jié)碼指令刨摩,而后找到對(duì)應(yīng)的ExceptionHandler進(jìn)行處理。

nmethod還有一個(gè)地方需要認(rèn)真處理世吨,即nmethod的“狀態(tài)”澡刹。一般概念上,一個(gè)東西同一時(shí)刻只能處于一種狀態(tài)中另假,但是nmethod的“狀態(tài)”比較特殊像屋。nmethod可能處于以下幾種狀態(tài)中:

  • active:正處于調(diào)用棧中,也可以被稱為是正在被使用边篮;
  • not-entrant:處于該狀態(tài),表名該nmethod將不能再被Java線程調(diào)度奏甫,但是如果此時(shí)的nmethod還處于調(diào)用棧中戈轿,那么這個(gè)nmethod也是active的;
  • zombie:僵尸狀態(tài)阵子,故名思議就是沒人用了思杯。zombie和active是互斥的;
  • for-reclamation:可以被回收的狀態(tài),處于這個(gè)狀態(tài)的nmethod可能會(huì)被sweeper線程回收色乾,釋放其空間誊册;

nmethod的這些狀態(tài),主要是在緩存清理的時(shí)候被使用到暖璧。

緩存清理——NMethodSweeper

CodCache滿了以后的管理案怯,是在CompileBroker里面實(shí)現(xiàn)的一铅。這個(gè)類是一個(gè)Broker模式寻馏。它承擔(dān)了JIT中的很多調(diào)度性質(zhì)的工作采驻,例如緩存滿了之后的處理假哎、編譯請(qǐng)求隊(duì)列管理筐高。我們現(xiàn)在只關(guān)注:

  //hotspot/src/share/vm/compiler/compileBroker.hpp
  static void handle_full_code_cache();

其實(shí)現(xiàn)里面關(guān)鍵的句子是:

    if (UseCodeCacheFlushing) {
      // Since code cache is full, immediately stop new compiles
      if (CompileBroker::set_should_compile_new_jobs(CompileBroker::stop_compilation)) {
        NMethodSweeper::log_sweep("disable_compiler");
      }
      // Switch to 'vm_state'. This ensures that possibly_sweep() can be called
      // without having to consider the state in which the current thread is.
      ThreadInVMfromUnknown in_vm;
      NMethodSweeper::possibly_sweep();
    } else {
      disable_compilation_forever();
    }

    // Print warning only once
    if (should_print_compiler_warning()) {
      warning("CodeCache is full. Compiler has been disabled.");
      warning("Try increasing the code cache size using -XX:ReservedCodeCacheSize=");
      codecache_print(/* detailed= */ true);
    }

這里也就是可以看出來喷兼,HotSpot如果在沒有設(shè)置允許緩存滿了的時(shí)候刷新緩存的話坛善,默認(rèn)是不再進(jìn)行任何的JIT工作佑附。這是一個(gè)極大的性能損失琅绅。普遍來說扶欣,會(huì)打印出日志“CodeCache is full. Compiler has been disabled”。

從這段代碼里面也可以看出來千扶,刷新緩存的關(guān)鍵在于NMethodSweeper::possibly_sweep();宵蛀。

NMethodSweeper置換nmethod(即清除代碼緩存)有兩個(gè)步驟:

  • 標(biāo)記active的方法。所謂的active的方法县貌,是指這個(gè)方法處于線程的調(diào)用棧中术陶。這個(gè)步驟必須要在safepoint中進(jìn)行。這是一個(gè)不難理解的步驟煤痕,如果一個(gè)方法還處于調(diào)用棧中梧宫,那么就意味著這個(gè)方法已經(jīng)被執(zhí)行了。如果這個(gè)時(shí)候被置換出去了摆碉,那么JVM就會(huì)出錯(cuò)——原本這個(gè)位置放置的是可執(zhí)行的代碼塘匣,而被清楚掉之后,JVM如果還繼續(xù)往下執(zhí)行巷帝,那么會(huì)發(fā)生情況忌卤,只能看天意了;
  • 清理nmethod楞泼。這個(gè)是在sweep_code_cache()中完成的驰徊。為了回收這塊內(nèi)存:
    • 首先將nmethod標(biāo)記為not-entrant。處于這種狀態(tài)下的nmethod將無法再被Java線程調(diào)用堕阔,但是它們可能是active的棍厂;
    • 這時(shí)候需要等待下一次的標(biāo)記,這是為了避免回收active的nmethod超陆。如果標(biāo)記發(fā)現(xiàn)它們不是active的牺弹,那么就可以將其標(biāo)記為zombie;
    • 所有的內(nèi)聯(lián)緩存(inline cache),如果引用了這個(gè)nmethod张漂,也需要被清理掉晶默。這主要是為了避免方法內(nèi)聯(lián)之后,因?yàn)楸粌?nèi)聯(lián)的方法已經(jīng)被清理掉了航攒,而Inline cache卻以為還存在繼續(xù)被使用磺陡;
    • 回收nmethod的內(nèi)存;


      nmethod狀態(tài)

顯而易見的屎债,只有多次標(biāo)記完成之后仅政,才有可能真正回收一塊內(nèi)存。標(biāo)記次數(shù)與回收次數(shù)的比值收到緩存空間大小的影響盆驹。在源碼中對(duì)此有說明:

  // hotspot/src/share/vm/runtime/sweeper.cpp
  // Small ReservedCodeCacheSizes:  (e.g., < 16M) We invoke the sweeper every time, since
  //                                              the result of the division is 0. This
  //                                              keeps the used code cache size small
  //                                              (important for embedded Java)
  // Large ReservedCodeCacheSize :  (e.g., 256M + code cache is 10% full). The formula
  //                                              computes: (256 / 16) - 1 = 15
  //                                              As a result, we invoke the sweeper after
  //                                              15 invocations of 'mark_active_nmethods.
  // Large ReservedCodeCacheSize:   (e.g., 256M + code Cache is 90% full). The formula
  //                                              computes: (256 / 16) - 10 = 6.

其中的清理的調(diào)用鏈?zhǔn)牵?/p>

  • NMethodSweeper::possibly_sweep()
    • NMethodSweeper::sweep_code_cache()
      • NMethodSweeper::process_nmethod(nmethod *nm)
        • NMethodSweeper::release_nmethod(nmethod *nm)
          • nmethod::flush()
            • CodeCache::free(CodeBlob* cb)

這里有很多的細(xì)節(jié)圆丹,但是并不屬于博主這篇文章打算探討的內(nèi)容∏回顧前面我們談?wù)摰降木彺嬷脫Q策略辫封,現(xiàn)在細(xì)細(xì)一看,那么也很容易發(fā)現(xiàn)HotSpot使用的的策略就是滿時(shí)清理廉丽。這有點(diǎn)出乎意料倦微,在博主真的深入源碼讀這一段邏輯之前,我以為HotSpot會(huì)使用一些更加高效率的策略正压。后來一想欣福,HotSpot使用的很多技術(shù)其實(shí)都不是最優(yōu)的。比如說method-base總體上是不如trace-base焦履,但是實(shí)現(xiàn)簡單拓劝。此處也是類似,雖然性能有損失嘉裤,但是好處是實(shí)現(xiàn)簡單郑临。
另外要提及的一點(diǎn)是,JVM默認(rèn)緩存清理是被關(guān)閉的屑宠。也就是緩存滿了就滿了厢洞,HotSpot將不再編譯任何代碼。這是一個(gè)極大的性能損失(重要的事情多說幾遍)典奉。
還有一個(gè)問題是躺翻,因?yàn)閍ctive可能存在緩存空間的任何一個(gè)地方——回收nmethod的時(shí)候并沒有執(zhí)行壓縮,因此使用的空閑鏈表技術(shù)秋柄,難免會(huì)帶來內(nèi)存碎片的問題获枝。

影響因素

我總結(jié)了一下我認(rèn)為對(duì)性能影響比較大的幾個(gè)地方。

  1. 緩存空間大泻П省:代碼緩存空間會(huì)占據(jù)一部分內(nèi)存。如果代碼緩存空間過大,勢必會(huì)影響其余部分的內(nèi)存使用笨触。但是如果緩存空間過小懦傍,那么就意味著緩存命中率會(huì)進(jìn)一步下降,而且導(dǎo)致頻繁的緩存換入換出芦劣。這兩個(gè)地方都會(huì)極大影響應(yīng)用的性能粗俱。一個(gè)比較有意思的例子是,早期谷歌在其安卓系統(tǒng)上就陷入了這種困境虚吟。早期的安卓系統(tǒng)面臨著代碼緩存瘋狂增長的問題寸认,因此他們甚至建議在內(nèi)存受限的設(shè)備上禁用掉代碼緩存功能。不過這個(gè)操作會(huì)付出巨大的代價(jià)串慰,其系統(tǒng)的性能會(huì)明顯下降偏塞,用戶對(duì)此都能明顯感知到。(博主也不知道后面的版本有沒有解決這個(gè)問題邦鲫,以及怎么解決了這個(gè)問題)
  2. 緩存置換算法:很顯然灸叼,這個(gè)問題就有點(diǎn)像是操作系統(tǒng)里面的的內(nèi)存頁面置換算法。最為理想的情況下庆捺,是將現(xiàn)有緩存里面將來最不可能被使用的那部分緩存置換出去古今。不過,也正如頁面置換算法所面臨的困境一般滔以,我們只能根據(jù)過去的表現(xiàn)去評(píng)估未來哪部分緩存就不會(huì)再被使用捉腥。所以,對(duì)于表現(xiàn)良好的緩存置換算法來說你画,能夠提高緩存命中率抵碟,能夠及時(shí)清理掉不再是“熱點(diǎn)”的代碼。而糟糕的緩存置換算法撬即,則剛好相反立磁。HotSpot使用的置換算法有點(diǎn)讓我失望,不知道它將來會(huì)不會(huì)有改進(jìn)剥槐;
  3. 緩存單元:即塊唱歧。基本上來說粒竖,HotSpot的JIT都是以方法為單位的颅崩。顯然的是,方法長度是不可預(yù)見的蕊苗,有些方法長沿后,有些方法短。那么這些塊占據(jù)的緩存空間大小也是不確定的朽砰。這會(huì)帶來額外的緩存空間管理的困難尖滚。因?yàn)闊o論是寫入還是置換喉刘,都需要計(jì)算塊的大小。前面提到的內(nèi)存碎片問題漆弄,在塊大小不均勻的時(shí)候睦裳,會(huì)更加嚴(yán)重。還有另外的一個(gè)問題是撼唾,HotSpot是拒絕對(duì)大的方法進(jìn)行JIT編譯的廉邑,其默認(rèn)設(shè)置是8000字節(jié)。-XX:HugeMethodLimit可以修改這個(gè)限制倒谷。但是在產(chǎn)品環(huán)境下蛛蒙,這個(gè)選項(xiàng)是無效的,但是可以使用-XX:-DontCompileHugeMethods來關(guān)閉對(duì)大方法的限制渤愁。

8000在面向?qū)ο笳Z言里面應(yīng)該算是一個(gè)很大的數(shù)值了牵祟。普遍而言,面向?qū)ο笳Z言充滿了各種小方法猴伶。我大概估算了一下(純粹的感覺估算)课舍,8000字節(jié)的方法,應(yīng)該能夠支撐2000行左右代碼他挎。

后記

其實(shí)還有一個(gè)問題筝尾,我覺得有必要回答一下的。就是我認(rèn)為办桨,代碼緩存對(duì)性能的影響筹淫,應(yīng)該要遠(yuǎn)遠(yuǎn)大于垃圾回收器的影響。不過現(xiàn)在呢撞,業(yè)界的開發(fā)者對(duì)這方面重視不足损姜。我覺得原因還是出在,垃圾回收有眾多的選項(xiàng)給開發(fā)者選擇殊霞,可調(diào)整的內(nèi)容比較多摧阅。而相比之下,緩存管理能夠被開發(fā)者所影響的東西就很少绷蹲。甚至說棒卷,基本上是無能為力的。

一些研究者研究過JVM的代碼緩存命中祝钢,不足70%比规。所以,實(shí)際上在這方面拦英,提升的空間還是很大的蜒什。不過短期內(nèi)應(yīng)該是看不到VM開放更多選項(xiàng)給應(yīng)用開發(fā)者使用。jdk9已經(jīng)暴露了JIT的接口疤估,博主沒有搞過灾常,也不清楚里面有沒有暴露一下JIT代碼緩存管理的接口霎冯。

我想到現(xiàn)在流行的微服務(wù)架構(gòu),在代碼方面還是有極其鮮明的特征的岗憋。能否說通過修改JVM的源碼肃晚,來提高緩存性能锚贱。比如說可以將一些RPC框架的關(guān)鍵節(jié)點(diǎn)仔戈、QPS極高的服務(wù)代碼常駐在代碼緩存里面。這算是一個(gè)猜想拧廊〖嗯牵可惜的是國內(nèi)真的做JVM定制的公司其實(shí)沒有幾個(gè),也就是阿里做得比較好吧碾。

JVM選項(xiàng)

這里列出一些和代碼緩存相關(guān)的JVM選項(xiàng)凰盔。我想讀了這篇文章前面的內(nèi)容,應(yīng)該很容易理解這些JVM選項(xiàng)的意義倦春。

  1. -XX:InitialCodeCacheSize:設(shè)置代碼緩存的初始大小户敬,這個(gè)參數(shù)在intel處理器下,在client編譯器模式下是160KB睁本,而在server編譯器模式下是2496KB尿庐;
  2. -XX:ReservedCodeCacheSize:設(shè)置代碼緩存的大小呢堰;
  3. -XX:+UseCodeCacheFlushing:當(dāng)代碼緩存滿了的時(shí)候抄瑟,讓JVM換出一部分緩存以容納新編譯的代碼。在默認(rèn)情況下枉疼,這個(gè)選項(xiàng)是關(guān)閉的皮假。這意味著,在代碼緩存滿了的時(shí)候骂维,JVM會(huì)切換到純解釋器模式惹资,這對(duì)于性能來說,可以說是毀滅性的影響航闺;
  4. -XX:NmethodSweepCheckInterval:設(shè)置清理緩存的時(shí)間間隔褪测;
  5. -XX:+DontCompileHugeMethods:默認(rèn)不對(duì)大方法進(jìn)行JIT編譯;
  6. -XX:HugeMethodLimit: 默認(rèn)值是8000来颤,遺憾的是汰扭,在產(chǎn)品環(huán)境下,該值不允許被修改福铅;

從我寫了前面兩篇到現(xiàn)在已經(jīng)過去快一個(gè)月了萝毛,第三篇終于姍姍來遲。這倒不是我要棄坑——雖然這個(gè)坑的確很大滑黔,而是因?yàn)樽罱娴谋容^忙笆包。而且寫這篇的過程中遇到很多的問題环揽,讓我覺得第三篇其實(shí)不應(yīng)該是這篇。我接下來的第四篇要分析一下HotSpot的熱點(diǎn)探測技術(shù)庵佣。按照道理來說歉胶,它應(yīng)該處于本篇前面。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巴粪,一起剝皮案震驚了整個(gè)濱河市通今,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肛根,老刑警劉巖辫塌,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異派哲,居然都是意外死亡臼氨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門芭届,熙熙樓的掌柜王于貴愁眉苦臉地迎上來储矩,“玉大人,你說我怎么就攤上這事褂乍〕炙恚” “怎么了?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵树叽,是天一觀的道長舆蝴。 經(jīng)常有香客問我,道長题诵,這世上最難降的妖魔是什么洁仗? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮性锭,結(jié)果婚禮上赠潦,老公的妹妹穿的比我還像新娘。我一直安慰自己草冈,他們只是感情好她奥,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怎棱,像睡著了一般哩俭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拳恋,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天凡资,我揣著相機(jī)與錄音,去河邊找鬼谬运。 笑死隙赁,一個(gè)胖子當(dāng)著我的面吹牛垦藏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伞访,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼掂骏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了厚掷?” 一聲冷哼從身側(cè)響起弟灼,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蝗肪,沒想到半個(gè)月后袜爪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡薛闪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俺陋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豁延。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖腊状,靈堂內(nèi)的尸體忽然破棺而出诱咏,到底是詐尸還是另有隱情,我是刑警寧澤缴挖,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布袋狞,位于F島的核電站,受9級(jí)特大地震影響映屋,放射性物質(zhì)發(fā)生泄漏苟鸯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一棚点、第九天 我趴在偏房一處隱蔽的房頂上張望早处。 院中可真熱鬧,春花似錦瘫析、人聲如沸砌梆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咸包。三九已至,卻和暖如春杖虾,著一層夾襖步出監(jiān)牢的瞬間烂瘫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國打工亏掀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忱反,地道東北人泛释。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像温算,于是被迫代替她去往敵國和親怜校。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理注竿,因此不免有一些不準(zhǔn)確的地方茄茁,同時(shí)不同JDK版本的...
    高廣超閱讀 15,629評(píng)論 3 83
  • 由于原文太長,所以拆成兩部分巩割。 第一部分在這里 JVM 結(jié)構(gòu) Java 編寫的代碼通過下圖所展示的流程執(zhí)行裙顽。 類加...
    唐先僧閱讀 1,166評(píng)論 0 5
  • 一愈犹、JVM內(nèi)幕:Java虛擬機(jī)詳解(java se 7規(guī)范) 直接上圖,再逐步解釋闻丑。 上圖顯示的組件分兩個(gè)章節(jié)解釋...
    屈小勇閱讀 1,854評(píng)論 6 22
  • Java 虛擬機(jī)(Java virtual machine漩怎,JVM)是運(yùn)行 Java 程序必不可少的機(jī)制。JVM實(shí)...
    Rick617閱讀 867評(píng)論 0 0
  • 從三月份找實(shí)習(xí)到現(xiàn)在嗦嗡,面了一些公司勋锤,掛了不少,但最終還是拿到小米侥祭、百度叁执、阿里、京東矮冬、新浪谈宛、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,278評(píng)論 11 349