JVM-java虛擬機空間模型

  瞭解JVM,不只是在面試時增長概率,更是在開發過程進行優化調整時瞭解內存溢出等,所以須要知道這個是什麼東東,有什麼卵用?html

 

  朋友,看20分鐘的唄,瞭解下?(有木有發傳單的趕腳?嘻嘻~~)java

 

  正好前段時間看了《深刻理解Java虛擬機:JVM高級特性與最佳實踐(第2版)》這本書,並且在騰訊課堂聽了某位大大的講解,因此本篇記錄下,一是本身瞭解並筆記,二能幫助他人就更好了。(就是下面這本書...看的本身有點懷疑人生...)面試

  

  java發展? 算法

    學習java的朋友都清楚java的特性,」write Once,Run Anywhere」,而支持其此特性最大的一點就是java虛擬機(JVM)。編程

    1.  從1996年 Sun 公司發佈JDK1.0時,其中內置的虛擬機是Classic VM,它是第一款商用的虛擬機,這款虛擬機只能使用純解釋器方式來執行java代碼,每一方法,每一行代碼都必須進行編譯,編譯耗時很是高,這就形成了業界對Java語言運行很慢的印象。可是其可使用外掛JIT編譯器(Just In Time),但此時的即時編譯與純解釋編譯不能夠同時工做。緩存

    2.  1998年,JDK1.2版本中的虛擬機是Exact VM,通過java團隊的優化,採用精準式的內存管理,其執行系統已經具有現代高性能虛擬機的雛形,如:兩級即時編譯器、編譯器與解釋器混合工做模式等。安全

    PS:《深刻理解java虛擬機》一書中說起,JDK1.2中曾並存過三種虛擬機,Classic VM、Exact VM、Hotspot VM數據結構

        Classic VM只能之外掛的形式使用JIT編譯器,Exact VM、Hotspot VM內置了JIT編譯器。多線程

    3.  1999年,Hotspot VM發佈,實質上該虛擬機是由名爲「Longview Technologies」的小公司設計的,因爲其優異表現於1997年被Sun公司收購,Hotspot VM發佈時是做爲JDK1.2的附加程序提供的,但其後來成爲了JDK1.3及之後的其餘版本的默認虛擬機。ide

    PS:HotSpot VM如其名稱,熱點代碼探測能力一開始就是準確式GC(Garbar Collection)

       若是一個方法被頻繁調用,或方法中有效循環次數不少,將會分別觸發標準編譯和OSR(棧上替換)編譯動做。經過編譯器與解釋器恰當地協同工做,能夠在最優化的程序響應時間與最佳執行性能中取得平衡,並且無須等待本地代碼輸出才能執行程序,即時編譯的時間壓力也相對減少,這樣有助於引入更多的代碼優化技術,輸出質量更高的本地代碼。

    4.  自JDK 1.3始,Sun公司維持一個習慣,每隔兩年發佈一個JDK版本,以動物命名,期間發佈的修正版本則以昆蟲爲工程名稱。

    5.  大事發生了!!!2008年和2009年,Oracle公司分別收購了BEA公司和Sun公司,這樣Oracle就同時擁有了兩款優秀的Java虛擬機:JRockit VM和HotSpot VM。Oracle公司宣佈在不久的未來(大約應在發佈JDK 8的時候)會完成這兩款虛擬機的整合工做,使之優點互補。整合的方式大體上是在HotSpot的基礎上,移植JRockit的優秀特性,譬如使用JRockit的垃圾回收器與MissionControl服務,使用HotSpot的JIT編譯器與混合的運行時系統。

    6. 2014年,Oracle發佈了JDK1.8,此版本是JDK近幾年的穩定版本,也是自JDK1.5以來改動最大的一個版本,各公司一直在用該版本。

    7. 2017年、2018年分別發佈了Java九、Java10,這兩個版本屬短時間版本,本博主已處在跟不上的階段了,並且上個版本還沒徹底熟悉...惆悵...

    

  java虛擬機發展?

    能夠參考本篇:https://201610042321.iteye.com/blog/2337901

    1.Sun 公司  ---   Classic VM、Exact VM、Hotpost VM、KVM、Squawk VM、Maxine VM...

    2.BEA公司   ---  JRockit VM

    3.IBM公司    ---  J9 VM、K8 VM

    4.還有Apache Harmoney、Google Android Dalvik VM、Microsoft JVM等等好多的虛擬機...

 

  JVM是什麼?

     如上圖:

      JDK:Java Development Kit,是整個java的核心,包含了JRE,Java開發工具,Java編譯器等;

      JRE:Java Runtime Enviromennt,運行Java程序的必須環境,包含JVM與java程序須要的核心類庫;

      JVM:Java Virtual Machine,是Java實現跨平臺的最核心部分,將字節碼文件轉換爲系統機器指令,並解釋執行,JVM是運行在操做系統之上的,沒有直接與硬件交互。

 

   Java源程序編譯爲字節碼文件?

      平常編程的文件都是 *.java文件,而在JVM中運行的文件則須要是 *.class文件,這其中的過程是如何進行編譯的呢?

    其中具體的編譯過程能夠閱讀【龍書】《編譯原理》,這本書裏面講解了程序是如何進行編譯的,是很是牛掰的一本著做。

 

    亦可參考這兩篇:https://www.jianshu.com/p/b9d3f20a880e

            http://m.elecfans.com/article/668346.html

            https://blog.csdn.net/wy727764020/article/details/80411751

     編譯是將一種抽象程度更高的語言轉換爲更貼近計算機可以識別的語言的過程。

    Java屬高級語言,一是其更貼近人類語言,更易理解(語法);二是平臺無關性,無需兼容不一樣系統(跨平臺);三是無需管理內存,更關注業務實現(內存管理)。

    一般命令行窗口在對應的目錄下執行javac *.java命令,編譯成功會在當前目錄下生成對應的class字節碼文件,執行java *獲得執行後結果。

 

    java代碼執行過程:Java源程序通過編譯器編譯後變成字節碼,字節碼由虛擬機解釋執行,虛擬機將每一條要執行的字節碼送給解釋器,解釋器將其翻譯成特定機器上的機器碼,而後在特定的機器上運行。

 

    通用代碼編譯器的步驟:

    Java代碼編譯器的步驟:

 

       實質上,JVM在JIT編譯階段就作了相關的優化,如逃逸分析、 鎖消除、 鎖膨脹、 方法內聯、 空值檢查消除、 類型檢測消除、 公共子表達式消除等等。

    有興趣的朋友能夠搜索一下,順便了解一下此處相關的具體優化點。

  

  JVM加載class文件?

     加載,實質上是將字節碼(類的二進制字節流)文件由磁盤讀取至虛擬機內存中,再經過類加載器完成,整個過程包含了7個階段:

    具體過程是按以上順序開始的,但不是按順序進行完成,由於這些階段一般都是互相交叉地混合進行的,一般在一個階段執行的過程當中調用或激活另外一個階段。

    

    裝載

      該階段須要完成如下3步操做:

        1.經過ClassLoader在classPath中獲取*.class文件,將其以二進制流形式讀入JVM內存中;

        2.將此字節流表明的靜態存儲結構轉換爲方法區中的運行時數據結構;

        3.在(堆)內存中,生成表明該類的Class對象,做爲對方法區中的數據訪問入口。

      裝載階段是可控性最強的階段,開發人員可使用系統的類加載器,也可以使用自定義的類加載器來完成此階段(繼承ClassLoader)。

      類加載器分爲3大類:

        1.啓動類加載器:Bootstrap ClassLoader,是虛擬機的一部分,負責加載JDK/jre/lib下,或被-Xbootclasspath參數指定的路徑中的,而且能被虛擬機識別的類庫(如rt.jar,全部的java.*開頭的類均被Bootstrap ClassLoader加載)。啓動類加載器是沒法被Java程序直接引用的。

        2.擴展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載JDK\jre\lib\ext目錄中,或者由java.ext.dirs系統變量指定的路徑中的全部類庫(如javax.*開頭的類),開發者能夠直接使用擴展類加載器。
        3.應用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者能夠直接使用該類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序中默認的類加載器。
  

      這三種類加載器的關係以下圖,這幾者之間的關係並不是繼承,而是組合的關係。

      關於加載時,JVM的加載機制有3種:

        1.全盤負責:加載某一class時,會將其相關的其餘class也由該類加載器負責加載;

        2.雙親委派:先告知其父類,父類再告知其祖類,直至最頂層類加載器,最頂層加載成功,則返回,不然依次向下進行加載,若都沒有,則拋ClassNotFoundError;

        3.緩存機制:寶成全部加載過的class都被緩存,當程序中使用某個class時,類加載器會先從緩存中尋找該class,若不存在,則讀取該類二進制文件,轉換爲class對象後,再次放入緩存區,若修改了class,則不需重啓JVM,修改纔會再次生效。

      java虛擬機採用的加載機制是 - 雙親委派模型

      採用該機制的優勢是 - 採用雙親委派模式的是好處是Java類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係,經過這種層級關能夠避免類的重複加載

      提問:按照上述解釋,JVM明明是父類委派或者是單親委派,但爲何一直稱爲’雙親委派’呢???

 

    驗證

      主要是確保class文件的字節流符合JVM規範,且不會危害虛擬機自身的安全。主要包括四種驗證:文件格式驗證,元數據驗證,字節碼驗證,符號引用驗證。

      對虛擬機自身安全的一種保證機制,保證應用是否會被惡意入侵的一道重要的防線,越是嚴謹的驗證機制越安全。

 

    準備

      爲類的靜態變量分配內存,並設置默認初始值,這些內存都將在方法區中存在。

 

    解析

      虛擬機將常量池內的符號引用替換爲直接引用的過程, 解析動做主要針對類或接口、字段、類方法、接口方法四類符號引用進行

      符號引用:以一組符號來描述所引用的目標。符號引用能夠是任何形式的字面量,只要使用時能無歧義地定位到目標便可,符號引用和虛擬機的佈局無關。
      直接引用:能夠是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄,直接引用和虛擬機的佈局是相關的,若是有了直接引用,那麼直接引用的目標必定被加載到了內存中。

 

    初始化

      真正開始執行類中定義的Java程序代碼,主要是根據程序中的賦值語句主動爲類變量賦指定值。

    執行

      程序中方法之間的相互調用。

    卸載

      銷燬一個對象,通常狀況下中有JVM垃圾回收器完成。代碼層面的銷燬只是將引用置爲null。

 

     整個類加載過程當中,除了在裝載階段,可以使用自定義的類加載器參與外,其他階段所有都由虛擬機主導控制,在初始化階段才真正執行字節碼文件,

 

    若你能看到此處,那接下來的東西纔是真正的JVM的區域劃分介紹。  -.-

 

   JVM運行時數據區域劃分?

     在介紹JVM數據區域劃分時,先簡單的運行一個簡單地代碼,便於分析java文件 -> class文件 在 JVM之中的存在及執行關係。

    先用一個test來查看一下源文件編譯後的class文件之間的對應關係吧,以下圖:這種的二進制文件,反正本人(想起來不良人中的本人了)是看的費勁,能理解一點點的一點點...

    將左邊的 Test.java 進行 javac Test.java 編譯後,生成右邊的 Test.class 字節碼文件,若是須要進行查看,須要使用 javap -v Test.class 反解析字節碼文件,生成可看懂的文件。

    

 

    關於Javap命令反編譯後的文件具體信息能夠參考:https://www.jianshu.com/p/6a8997560b05

   

    當反編譯以後的字節碼文件,能夠參考JVM字節碼指令進行查詢相應的跳轉處理邏輯,其中關於鎖,i++ / ++i 等等操做原理均可以剖析下,上面的反編譯代碼至截取了當中的 main() 和 addAndTest() 這兩個方法,其餘的類元信息、主次版本號、常量信息,屬性信息等都沒有截取,有興趣的朋友能夠本身試一下,參照JVM指令查看一下反編譯後的文件。

 

    你們看到反編譯後的文件中有bipush、astore_一、aload_1等等指令,這些都是在JVM中進行操做的,該代碼是引出接下來的JVM模型的....

 

    看到這兒的朋友,10分鐘應該是過去了,也就是喝杯水休息的功夫,不過注意了,由於接下來就要變形了,呃,不對,是真正的JVM模型的講解了。

 

    先看一張JVM的自己內存結構圖,以下:

    (畫了好一下子吶)能夠參考下這篇博文,講解的仍是很是清楚的:https://www.nowcoder.com/discuss/151138?type=1

    線程計數器:

      程序計數器是一塊較小的內存空間,可看做當前線程正在執行的字節碼的行號指示器。若當前線程正在執行的是Java方法,計數器記錄的就是當前線程正在執行的字節碼指令的地址(行號);如果本地Native方法,那麼程序計數器值爲undefined

      程序計數器有兩個做用:字節碼解釋器經過改變程序計數器來依次讀取指令,從而實現代碼的流程控制,如:順序執行、選擇、循環、異常處理;在多線程的狀況下,程序計數器用於記錄當前線程執行的位置,從而當線程被來回切換時清楚該線程運行位置,即線程切換後能恢復到正確的執行位置。

      線程私有。每條線程都有一個獨立的程序計數器,並且是JVM中惟一一個不會出現OOM的內存區域。生命週期隨着線程的建立而建立,隨着線程的結束而死亡。

 

      思考:線程計數器指的是當前線程的字節碼行號指示器,那爲何能夠根據它來選取下一條須要執行的字節碼指令?

       答:記錄當前線程所執行的字節碼行號,用於獲取下一條執行的字節碼。在每次指令執行後自增, 維護下一個將要執行指令的地址,若當前爲最後的行號,則其會根據最後的return語句進入下一個要執行的線程位置,再獲取當前線程此位置的行號。

 

    Java 棧:

         虛擬機棧描述的是Java方法執行的內存區域,也是線程私有的: 每一個方法被執行時會建立一個棧幀(Stack Frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。
      棧中的元素用於支持虛擬機進行方法調用,每一個方法從開始調用到執行完成的過程,就是棧幀在虛擬機棧中從入棧到出棧的過程
      在活動線程中,只有位於棧頂的幀纔是有效的,稱爲當前棧幀,在執行引擎運行時,全部指令都只能針對當前棧幀進行操做;正在執行的方法稱爲當前方法;棧幀是方法運行的基本結構,其壓棧彈棧方式是FILO(First In Last Out)。

      關於運行時的棧幀結構,能夠參考一下這篇博文:http://www.javashuo.com/article/p-wmhvmenl-du.html

      Java虛擬機棧會出現兩種異常:
        StackOverFlowError:若Java虛擬機棧的內存大小不容許動態擴展,那麼當線程請求的棧深度大於虛擬機容許的最大深度時(但內存空間可能還有不少),就拋出此異常;
        OutOfMemoryError:若Java虛擬機棧的內存大小容許動態擴展,且當線程請求棧時內存用完了,沒法再動態擴展了,此時拋出OutOfMemoryError異常;
      Java虛擬機棧也是線程私有的,每一個線程都有各自的Java虛擬機棧,並且隨着線程的建立而建立,隨着線程的死亡而死亡。

 

 

    本地方法棧:

      本地方法棧與虛擬機棧所發揮的做用是很是類似的,其區別不過是Java虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。虛擬機規範中對本地方法棧中的方法使用的語言、使用方式與數據結構並無強制規定,所以具體的虛擬機能夠自由實現它。甚至有的虛擬機(譬如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。與虛擬機棧同樣,本地方法棧區域也會拋出StackOverflowError和OutOfMemoryError異常

      該區域也是線程私有的,對JVM而言,java虛擬機棧「主內」,而本地方法棧「主外」,本地方法能夠經過JNI(Java Native Interface)來訪問虛擬機運行時的數據區,甚至能夠調用寄存器,具備和JVM相同的能力和權限,當大量本地方法出現時,勢必會削弱JVM對系統的控制力,大量使用其餘語言來實現JNI,就會喪失跨平臺特性,威脅到程序運行的穩定性,由於它的出錯信息都比較黑盒。

 

 

    java 堆:

      Java虛擬機所管理的內存中最大且被全部線程共享的一塊內存區域,在虛擬機啓動時建立。此內存區域的惟一目的就是存放對象實例,幾乎全部的對象實例都在這裏分配內存。

 

      Java堆是垃圾收集器管理的主要區域,所以不少時候也被稱爲「GC堆」(Garbage Collected Heap)。從內存回收的角度來看,因爲如今收集器基本都採用分代收集算法,因此Java堆還能夠細分爲:新生代和老年代;新生代又能夠分爲:Eden 空間、From Survivor空間、To Survivor空間。若是從內存分配的角度看,線程共享的Java 堆中可能劃分出多個線程私有的分配緩衝區Thread Local Allocation Buffer,TLAB)。根據Java虛擬機規範的規定,Java堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可,就像咱們的磁盤空間同樣。在實現時,既能夠實現成固定大小的,也能夠是可擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(經過-Xms和-Xmx控制)。若是在堆中沒有內存完成實例的分配,而且堆也沒法再擴展時,將會拋出OutOfMemoryError異常。

 

      此處關於堆的空間就少介紹一點吧,等到後面再開一篇博客詳細講解關於堆中的具體信息,本篇主要關注的是堆中的東西。也就是對象在堆中是如何存在的?

 

      那對象是如何建立的呢?以下圖:

      在HotspotVM中,對象在內存中存儲的佈局能夠分爲3塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。

         1.對象頭

          第一部分用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等,官方稱之爲MarkWord;


          另一部分是類型指針,即對象指向它的類元數據的指針, 虛擬機經過這個指針來肯定這個對象是哪一個類的實例實例數據。

         2.實例數據

          對象真正存儲的有效信息,也是在程序代碼中所定義的各類 類型的字段內容。 不管是從父類繼承下來的,仍是在子類中 定義的,都須要記錄起來對齊填充。

        3.對齊填充

          非必要存在, 僅起到佔位符的做用, 緣由是HotSpot自動內存管理系統要求對象起始地址必須是8字節的整數倍, 即對象的大小必須是8字節的整數倍.

 

      對象的訪問定位:

        JVM 規範中並無詳細規定引用類型的實現細節,好比引用應該經過何種方式去定位、訪問堆中的對象,具體的對象訪問方式取決於虛擬機的具體實現,好比 HotSpot 有其本身的實現方案。目前主流的訪問方式有使用句柄和直接指針兩種,以下圖:

 

    方法區:非堆 / 永久代

      方法區是堆空間的一個外延部分,但並不是屬於堆,所以還可稱其爲 ' Non-Heap(非堆) ',方法區中存放已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

      方法區中的信息通常須要長期存在,並且它又是堆的邏輯分區,所以用堆的劃分方法,咱們把方法區稱爲‘ 永久代 (Permanent Generation)’。這部分空間大小可擴展,其中數據是線程共享的。

        HotSpot VM虛擬機把GC分代收集擴展至方法區, 即便用Java堆的永久代來實現方法區,這樣HotSpot的垃圾收集器就能夠像管理Java堆同樣管理這部份內存, 而沒必要爲方法區開發專門的內存管理器(永久帶的內存回收的主要目標是針對常量池的回收和類型的卸載, 所以收益通常很小)。只有Hotspot纔有Perm區(永久代),它在啓動時固定大小,很難進行調優,而且Full GC時會移動類元信息。而對於其餘虛擬機(如BEA JRockit、IBM J9 等)來講是不存在永久代的概念的。

         運行時常量池:

          運行時常量池是方法區的一部分。其中方法區中的常量存儲在運行時常量池中

          常量池( Constant pool table)中存放編譯時期產生的各類字面量和符號引用,.class文件中的常量池中的全部的內容在類被加載後存放到方法區的運行時常量池中。運行時常量池相對於class文件常量池的另一個特性是具有動態性,java語言並不要求常量必定只有編譯器才產生,也就是並不是預置入class文件中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中。如String類中的intern()方法就是採用了運行時常量池的動態性。當調用 intern 方法時,若是池已經包含一個等於此 String 對象的字符串,則返回池中的字符串。不然,將此 String 對象添加到池中,並返回此 String 對象的引用,就是在運行期間向常量池中添加字符串常量

          在近三個JDK版本(六、七、8)中, 運行時常量池的所處區域一直在不斷的變化,在JDK6時它是方法區的一部分,7又把他放到了堆內存中,8以後出現了元空間,它又回到了元空間

          運行時常量池是方法區的一部分,因此會受到方法區內存的限制,所以當常量池沒法再申請到內存時就會拋出OutOfMemoryError異常。當運行時常量池中的某些常量沒有被對象引用,同時也沒有被變量引用,那麼就須要垃圾收集器回收

 

 

    元空間:JDK1.8以後代替方法區的區域。

      在JDK8以後,永久代被移除,本來存儲在永久代的數據將存放在一個叫作元空間(Metaspace)的本地內存區域

      在Java虛擬機(如下簡稱JVM)中,類包含其對應的元數據,好比類的層級信息,方法數據和方法信息(如字節碼,棧和變量大小),運行時常量池,已肯定的符號引用和虛方法表。在過去(當自定義類加載器使用不廣泛的時候),類幾乎是「靜態的」而且不多被卸載和回收,所以類也能夠被當作「永久的」。另外因爲類做爲JVM實現的一部分,它們不禁程序來建立,由於它們也被認爲是「非堆」的內存。

      若是在JDK8以前碰到java.lang.OutOfMemoryError: PermGenspace,爲解決該問題,須要設置永久代的運行參數:-XX:MaxPermSize= l280m。若將此部署到新機器上,也須要再次調參數信息,因此在JDK8中使用元空間替換永久代。區別於永久代,元空間在本地內存中分配,即:只要本地內存足夠,不會出現像永久代中 PermGenspace一樣的,對永久代的設置參數PermSize和MaxPermSize也會失效。

      默認狀況下,「元空間」的大小能夠動態調整,或者使用新參數MaxMetaspaceSize來限制本地內存分配給類元數據的大小

      在JDK8裏,Perm 區全部內容中,字符串常量移至堆內存,其餘內容包括類元信息、字段、靜態屬性、方法、常量等都移動至元空間。

 

       元空間的特色:

         此區域沒有GC掃描,Full GC時,指向元數據指針都不用再掃描(CMS不會掃描元空間),減小了Full GC的時間,同時減小了內存碎片;

         元空間的對象不會進行轉移,也不進行壓縮,減小數據轉移壓縮的開銷;

         除非某個類加載器死亡,不然不會對此區域進行回收;

 

 

  JVM關閉?

    正常狀況下,當隨後的非守護線程結束,或調用退出指令(如System.exit(); )JVM正常關閉;

    若系統運行遇到異常信息,拋出RuntimeException時,也會退出,屬於異常關閉;

    還有強制執行kill命令,殺死進程,或者是調用Runtime.halt(),這種屬於強制關閉。

 

 

  JVM如何調優?

    啥?你問爲何須要JVM優化?這我TM...唉...

      確定是程序出問題了呀,否則還能怎麼吶?如程序運行卡停、無反應、CPU負載忽然太高... ... 

 

     一般進行JVM調優時,都會先清楚是哪部分代碼的問題,這是就須要使用命令或者使用工具來進行排查了。

       命令行工具,這個沒有具體使用過;可使用Jconsole、JVisualVM等來查看java線程執行(如熱點代碼分析、內存泄漏檢查等),此處就不介紹了...嘿嘿,由於工具嘛,使用也不難...

      JVM相關調優連接:https://pengjiaheng.iteye.com/blog/552456 

                 http://baijiahao.baidu.com/s?id=1601240621385314413&wfr=spider&for=pc

 

    具體如何調整呢?

      最終的結果是使用現有的硬件消耗承載最大的系統請求吞吐量

      主要是達到兩個目的:一使GC次數減小,二使GC時間減小。與其說減小,不如說過度的少。不過這二者之間的關係是相互矛盾的,所以須要在二者之間進行性能權衡,固然也能夠根據不一樣的GC算法來選擇不一樣的GC收集器。具體調整是根據當前線程的執行狀況來決定。

 

 

    本節中有部分未完善,會在後續博文中更新...煩請稍做等待...

 

 

  (願你的每一行代碼,都有讓世界進步的力量    ------   fn)

相關文章
相關標籤/搜索