(JVM)學(xué)習(xí)筆記(不定時(shí)更新))
Java虛擬機(jī)JVM學(xué)習(xí)筆記不少組織都曾開(kāi)發(fā)過(guò)Java虛擬機(jī)SUN公司曾經(jīng)使用過(guò)3個(gè)虛擬機(jī)Classic、Exact VM、Hotspot。其中Hotspot虛擬機(jī)沿用至今并已被Oracle合并到其新的虛擬機(jī)平臺(tái)中與同樣被Oracle收購(gòu)的BEA公司的JRocket虛擬機(jī)合并。IBM的Java虛擬機(jī)叫J9。Apache也曾推出過(guò)一款叫做Apache Harmony的Java運(yùn)行平臺(tái)受到同樣開(kāi)源的OpenJDK的排擠已于2011年退出市場(chǎng)。Java虛擬機(jī)的基本結(jié)構(gòu)1、類加載子系統(tǒng)2、Java內(nèi)存程序計(jì)數(shù)器、Java堆、Java棧、本地方法棧、方法區(qū)、直接內(nèi)存3、垃圾回收子系統(tǒng)4、執(zhí)行引擎。Java堆一個(gè)Java程序的幾乎所有對(duì)象都存放在堆中并且堆是完全自動(dòng)化管理的不需要顯式釋放堆中的垃圾對(duì)象。在不同的虛擬機(jī)中或在不同的垃圾回收機(jī)制下Java堆可能具有不同的結(jié)構(gòu)特征但最常見(jiàn)的組織結(jié)構(gòu)是把整個(gè)堆分為新生代和老年代tenured。新生代中存放新創(chuàng)建的對(duì)象和“年齡”不大的對(duì)象老年代則存放“年齡”足夠大的對(duì)象。新生代又分為eden、from、to三個(gè)區(qū)域其中from和to是兩塊大小相等、可以互換角色的內(nèi)存空間。通常新創(chuàng)建的對(duì)象在eden區(qū)分配內(nèi)存在一次新生代回收后如果對(duì)象還存活則會(huì)被轉(zhuǎn)移到from區(qū)或to區(qū)。對(duì)象進(jìn)入from區(qū)或to區(qū)之后沒(méi)經(jīng)過(guò)一次新生代回收如果對(duì)象還存活著則它的“年齡”加1。當(dāng)“年齡”增長(zhǎng)到一定程度后會(huì)被轉(zhuǎn)移到老年代。Java棧Java棧是線程私有的內(nèi)存空間。函數(shù)調(diào)用是線程執(zhí)行的基本行為每次函數(shù)調(diào)用所需的數(shù)據(jù)都是通過(guò)Java棧傳遞的。Java棧和數(shù)據(jù)結(jié)構(gòu)課程中所描述的棧有類似的特征都是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu)只支持入棧和出棧兩種操作。棧幀是Java棧的基本單元。每次函數(shù)調(diào)用都會(huì)有一個(gè)棧幀入棧每次函數(shù)調(diào)用結(jié)束都會(huì)有一個(gè)棧幀出棧。無(wú)論函數(shù)是正常返回還是拋出異常都會(huì)有一個(gè)棧幀出棧。棧幀在一個(gè)棧幀中至少包含3個(gè)部分局部變量表、操作數(shù)棧、幀數(shù)據(jù)區(qū)。由于每次函數(shù)調(diào)用都會(huì)有棧幀入棧從而占據(jù)更多的棧空間顯然如果棧空間不足以放置更多的棧幀時(shí)函數(shù)調(diào)用將無(wú)法繼續(xù)下去這個(gè)時(shí)候會(huì)拋出StackOverflowError。Java虛擬機(jī)提供了“-Xss”參數(shù)用于在虛擬機(jī)啟動(dòng)的時(shí)候配置棧空間的最大值這個(gè)最大值決定了函數(shù)嵌套調(diào)用的深度。局部變量表局部變量表是棧幀的重要組成部分它用于保存函數(shù)的參數(shù)和局部變量。由于局部變量表在棧幀中如果函數(shù)的參數(shù)和局部變量過(guò)多會(huì)造成局部變量表膨脹從而棧幀變大函數(shù)嵌套調(diào)用的深度也會(huì)降低。操作數(shù)棧操作數(shù)棧也是棧幀的組成部分之一它被用來(lái)保存計(jì)算的中間結(jié)果作為計(jì)算過(guò)程中變量的臨時(shí)存儲(chǔ)空間。操作數(shù)棧也是先進(jìn)后出的很多Java字節(jié)碼指令都需要通過(guò)操作數(shù)棧進(jìn)行參數(shù)傳遞。幀數(shù)據(jù)區(qū)幀數(shù)據(jù)區(qū)中保存著常量池的指針?lè)奖阕止?jié)碼指令訪問(wèn)常量池。幀數(shù)據(jù)區(qū)中保存了一張異常處理表方便在發(fā)生異常時(shí)找到處理異常的代碼。棧上分配棧上分配是Java虛擬機(jī)提供的優(yōu)化技術(shù)。它的思路是可以把某些線程私有對(duì)象分配在棧上借助退棧操作直接清理該對(duì)象而不需要垃圾回收子系統(tǒng)的介入減少GC從而提高系統(tǒng)性能。棧上分配的技術(shù)基礎(chǔ)是逃逸分析逃逸分析用于判斷某個(gè)對(duì)象是否能夠被其他線程訪問(wèn)到。如果某個(gè)對(duì)象能夠被其他線程訪問(wèn)到則它是一個(gè)逃逸對(duì)象不能分配在棧上。順便提一下書(shū)上說(shuō)只有Java虛擬機(jī)運(yùn)行在Server模式下的時(shí)候才可以啟用逃逸分析。方法區(qū)方法區(qū)和Java堆一樣是線程共享的內(nèi)存區(qū)域。它主要用于保存Java類信息字段、方法等。方法區(qū)的大小決定了虛擬機(jī)中可以加載多少Java類。如果有太多的Java類需要加載通常使用了某些動(dòng)態(tài)代理技術(shù)時(shí)會(huì)動(dòng)態(tài)產(chǎn)生過(guò)多的類信息會(huì)拋出內(nèi)存溢出錯(cuò)誤。虛擬機(jī)參數(shù)-XX:PermSize和-XX:MaxPermSize用于指定方法區(qū)的最小和最大尺寸。默認(rèn)情況下最大64MB。在JDK1.6和1.7中方法區(qū)也叫永久區(qū)。在JDK1.8、1.9和1.10中永久區(qū)已被“元數(shù)據(jù)區(qū)”取代元數(shù)據(jù)區(qū)是直接內(nèi)存。和永久區(qū)不同的是元數(shù)據(jù)區(qū)可以擴(kuò)展至耗盡所有系統(tǒng)內(nèi)存。跟蹤調(diào)試參數(shù)Java虛擬機(jī)提供了大量用于跟蹤系統(tǒng)狀態(tài)的參數(shù)配置使用這些參數(shù)可以在Java程序運(yùn)行過(guò)程中打印相關(guān)日志用于問(wèn)題分析。GC參數(shù)GC垃圾回收機(jī)制是Java的一大特色可以幫助程序員管理內(nèi)存。但是當(dāng)GC對(duì)程序本身造成影響GC過(guò)于頻繁、CPU過(guò)載等時(shí)需要進(jìn)行分析處理。打印GC日志-XX:PrintGC使用這個(gè)參數(shù)啟動(dòng)虛擬機(jī)只要遇到GC就會(huì)打印日志。-Xlog:gc這個(gè)參數(shù)是用來(lái)給JDK9和JDK10打印GC日志的因?yàn)檫@兩個(gè)版本的JDK默認(rèn)使用G1收集器。打印詳細(xì)GC日志的參數(shù)是-XX:PrintGCDetailsJDK9和JDK10中對(duì)應(yīng)的參數(shù)是-Xlog:gc*。跟蹤類的加載、卸載-verbose:class參數(shù)可以跟蹤類的加載和卸載。單獨(dú)使用-XX:TraceClassLoading參數(shù)JDK9和JDK10中對(duì)應(yīng)的參數(shù)是-Xlog:classloadinfo跟蹤類的加載情況。單獨(dú)使用-XX:TraceClassUnloading參數(shù)JDK9和JDK10中對(duì)應(yīng)的參數(shù)是-Xlog:classunloadinfo跟蹤類的卸載情況。查看系統(tǒng)參數(shù)-XX:PrintVMOptions參數(shù)在程序運(yùn)行時(shí)打印虛擬機(jī)接收到的命令行顯式參數(shù)列表。-XX:PrintCommandLineFlags參數(shù)打印傳遞給虛擬機(jī)的顯式和隱式參數(shù)。-XX:PrintFlagsFinal參數(shù)打印所有的系統(tǒng)參數(shù)值。Java堆大小設(shè)置當(dāng)Java進(jìn)程啟動(dòng)時(shí)虛擬機(jī)會(huì)給該進(jìn)程分配一塊初始堆空間當(dāng)初始堆空間耗盡后虛擬機(jī)會(huì)擴(kuò)展堆空間擴(kuò)展后的堆空間大小不能超過(guò)最大堆空間。-Xms參數(shù)在Java進(jìn)程啟動(dòng)時(shí)指定初始堆空間大小。-Xmx參數(shù)指定最大堆空間大小。新生代的配置-Xmn參數(shù)用于設(shè)置新生代的大小。-XX:SurvivorRatio參數(shù)用于指定新生代中eden區(qū)和from/to區(qū)的比例即-XX:SurvivorRatioeden/fromeden/to新生代的大小一般設(shè)置為整個(gè)堆空間的四分之一到三分之一新生代的大小對(duì)GC有很大影響基本上應(yīng)該盡可能將對(duì)象預(yù)留在新生代以減少老年代GC的次數(shù)。設(shè)置較大的新生代會(huì)減小老年的大小。-Xmn參數(shù)可以指定新生代的絕對(duì)大小還可以使用-XX:NewRatio參數(shù)來(lái)設(shè)置新生代和老年代的比例-XX:NewRatio老年代/新生代堆溢出Java程序在運(yùn)行過(guò)程中如果遇到堆內(nèi)存不足的情況時(shí)則會(huì)拋出內(nèi)存溢出錯(cuò)誤OOMOut Of Memory。對(duì)于OOMJava虛擬機(jī)提供了-XX:HeapDumpOnOutOfMemoryError參數(shù)用于在發(fā)生OOM時(shí)導(dǎo)出整個(gè)堆信息。這個(gè)參數(shù)配合另一個(gè)參數(shù)-XX:HeapDumpPath指定導(dǎo)出堆的存放路徑j(luò)ava -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPathd:/java.dump ...另外還有一個(gè)參數(shù)-XX:OnOutOfMemoryError在發(fā)生錯(cuò)誤時(shí)調(diào)用一個(gè)腳本文件用于善后處理。java -XX:OnOutOfMemoryErrord:/abc.bat ...方法區(qū)配置在JDK1.6和JDK1.7中可以使用下面兩個(gè)參數(shù)設(shè)置方法區(qū)大小的下限和上限-XX:PermSize-XX:MaxPermSizeJDK1.8以后方法區(qū)已被廢棄類信息存放在元數(shù)據(jù)區(qū)中。元數(shù)據(jù)區(qū)只受系統(tǒng)可用內(nèi)存的限制。可以使用-XX:MaxMetaspaceSize參數(shù)設(shè)置元數(shù)據(jù)區(qū)的最大可用值。直接內(nèi)存配置直接內(nèi)存跳過(guò)Java堆直接操作系統(tǒng)原生內(nèi)存。從一定程度上加快了內(nèi)存的訪問(wèn)速度。但是認(rèn)為使用直接內(nèi)存一定可以提高性能是錯(cuò)誤的。一般來(lái)說(shuō)直接內(nèi)存的訪問(wèn)速度比Java堆內(nèi)存要快但是在內(nèi)存申請(qǐng)時(shí)直接內(nèi)存毫無(wú)優(yōu)勢(shì)可言。所以看直接內(nèi)存適合申請(qǐng)次數(shù)較少、訪問(wèn)較頻繁的場(chǎng)景。-XX:MaxDirectMemorySize參數(shù)可以限定直接內(nèi)存的最大值如果不設(shè)置這個(gè)參數(shù)則默認(rèn)最大值為-Xmx參數(shù)的值。當(dāng)已使用的直接內(nèi)存到達(dá)指定的最大值時(shí)會(huì)觸發(fā)GCGC后如果仍舊內(nèi)存不足會(huì)引起OOM。Java虛擬機(jī)的工作模式Java虛擬機(jī)支持Client和Server兩種運(yùn)行模式。這兩種運(yùn)行模式可以通過(guò)兩個(gè)參數(shù)分別指定-client參數(shù)-server參數(shù)因?yàn)橐許erver模式啟動(dòng)Java程序時(shí)會(huì)嘗試手機(jī)更多的系統(tǒng)性能信息使用更復(fù)雜的優(yōu)化算法所以Server模式啟動(dòng)程序的速度要慢于Client模式。但是也正是因?yàn)檫@樣以Server模式啟動(dòng)的Java程序要遠(yuǎn)遠(yuǎn)快于以Client模式啟動(dòng)的Java程序。常見(jiàn)垃圾回收算法1、引用計(jì)數(shù)法Reference Counting2、標(biāo)記清除法Mark-Sweep3、復(fù)制算法Copying4、標(biāo)記壓縮法Mark-Compact5、分代算法Generationall Collecting6、分區(qū)算法Region。引用計(jì)數(shù)法引用計(jì)數(shù)法是最經(jīng)典且古老的一種垃圾收集算法。引用計(jì)數(shù)法的基本思想是給對(duì)象設(shè)置一個(gè)引用次數(shù)的變量。對(duì)象被賦值給其他變量引用次數(shù)加一引用失效后引用次數(shù)減一。但引用次數(shù)變成零的時(shí)候代表對(duì)象不再被任何變量引用可以直接回收該對(duì)象占用的內(nèi)存空間。引用計(jì)數(shù)法的最嚴(yán)重問(wèn)題是無(wú)法處理循環(huán)引用的問(wèn)題。標(biāo)記清除法標(biāo)記清除法是現(xiàn)代垃圾收集算法的思想基礎(chǔ)。標(biāo)記清除法分兩個(gè)階段標(biāo)記階段和清除階段。在標(biāo)記階段算法通過(guò)根節(jié)點(diǎn)標(biāo)記所有從根節(jié)點(diǎn)開(kāi)始的可達(dá)對(duì)象。在清除階段回收所有未被標(biāo)記的對(duì)象所占用的內(nèi)存空間。標(biāo)記清除法的最大問(wèn)題是可能產(chǎn)生大量的內(nèi)存碎片。復(fù)制算法復(fù)制算法的核心思想是將這個(gè)內(nèi)存空間分成兩塊每次只使用其中的一塊。當(dāng)需要垃圾回收時(shí)將正在使用的那一塊中所有的存活對(duì)象都復(fù)制到另一塊內(nèi)存空間中然后清理當(dāng)前內(nèi)存塊中的所有對(duì)象。之后交換兩塊內(nèi)存的角色GC完成。復(fù)制算法非常符合新生代垃圾回收的要求因?yàn)樾律锏睦鴮?duì)象通常遠(yuǎn)遠(yuǎn)多于存活對(duì)象因此更有效率。之前介紹過(guò)新生代被分成了三塊eden、from、to。其中的from區(qū)和to區(qū)可以視為復(fù)制算法中的兩塊大小相同、地位相等且可以互換角色的內(nèi)存空間。標(biāo)記壓縮法標(biāo)記壓縮法是一種老年代的回收算法。它是標(biāo)記清除法的優(yōu)化版。標(biāo)記壓縮法在執(zhí)行了標(biāo)記清除法的兩個(gè)階段后還會(huì)把存活對(duì)象壓縮到內(nèi)存的一端從而避免了碎片的產(chǎn)生。也就是說(shuō)標(biāo)記壓縮法標(biāo)記清除法碎片整理。分代算法分代算法本身并不是一種新的垃圾回收算法。它的思路是根據(jù)對(duì)象的特點(diǎn)把內(nèi)存分成不同的塊然后根據(jù)每塊內(nèi)存中對(duì)象的特點(diǎn)選擇不同的垃圾回收算法以提高效率。分代算法被現(xiàn)代虛擬機(jī)廣泛使用幾乎所有的垃圾回收器都區(qū)分新生代和老年代。通常新生代會(huì)頻繁發(fā)生垃圾回收但是每次垃圾回收耗時(shí)都比較短而老年代的垃圾回收并不頻繁但是只要發(fā)生垃圾回收都會(huì)消耗很長(zhǎng)時(shí)間。分區(qū)算法通常內(nèi)存空間越大一次垃圾回收的耗時(shí)就越長(zhǎng)。分區(qū)算法的思路是把這個(gè)內(nèi)存空間分割成若干小的區(qū)間每次需要垃圾回收時(shí)都選擇其中的部分小區(qū)間進(jìn)行回收而不是回收整個(gè)內(nèi)存空間。這樣就有效控制了每次垃圾回收所產(chǎn)生的停頓時(shí)間。可觸及性一個(gè)相對(duì)是否可以被當(dāng)做垃圾回收需要考察這個(gè)對(duì)象的可觸及性即在任何情況下從根節(jié)點(diǎn)開(kāi)始是否可以訪問(wèn)到這個(gè)對(duì)象。如果在任何情況下從所有根節(jié)點(diǎn)開(kāi)始都無(wú)法直接或間接訪問(wèn)到這個(gè)對(duì)象則說(shuō)明這個(gè)對(duì)象已經(jīng)不再被使用了該對(duì)象可以被回收。可觸及性設(shè)計(jì)三種狀態(tài)1、可觸及從根節(jié)點(diǎn)開(kāi)始可以到達(dá)該對(duì)象則稱該對(duì)象可觸及2、可復(fù)活對(duì)象的所有引用都已釋放但是該對(duì)象可以通過(guò)調(diào)用其finalize方法被復(fù)活3、不可觸及finalize方法已被調(diào)用finalize只可能被虛擬機(jī)調(diào)用一次但沒(méi)有被復(fù)活該對(duì)象就進(jìn)入不可觸及狀態(tài)。finalize方法finalize方法是一個(gè)非常糟糕的反模式不推薦使用它來(lái)釋放資源。首先f(wàn)inalize方法很可能在無(wú)意中復(fù)活對(duì)象。其次finalize方法是由系統(tǒng)調(diào)用的調(diào)用時(shí)機(jī)不明確。引用強(qiáng)度Java中提供了4個(gè)級(jí)別的引用1、強(qiáng)引用2、軟引用3、弱引用4、虛引用。強(qiáng)引用強(qiáng)引用就是一般的引用它不會(huì)被垃圾回收器回收。軟引用java.lang.ref.SoftReference軟引用比強(qiáng)引用稍微弱一點(diǎn)。如果某對(duì)象只持有軟引用那么當(dāng)堆空間不足時(shí)就會(huì)被回收因此軟引用對(duì)象不會(huì)引起內(nèi)存溢出。弱引用java.lang.ref.WeakReference弱引用比軟引用還要弱。在垃圾回收時(shí)不管系統(tǒng)內(nèi)存的使用情況如何只要發(fā)現(xiàn)弱引用就會(huì)立即對(duì)其進(jìn)行回收。弱引用之所以可以在內(nèi)存中存在是因?yàn)槔厥站€程的優(yōu)先級(jí)很低不能立即發(fā)現(xiàn)它。虛引用java.lang.ref.PhantomReference虛引用是所有引用類型中最弱的一個(gè)。虛引用和沒(méi)有引用幾乎是一樣的隨時(shí)都可能被回收。試圖通過(guò)虛引用的get方法取得強(qiáng)引用時(shí)總會(huì)失敗。虛引用只是為了跟蹤垃圾回收過(guò)程。垃圾回收器1、串行回收器2、并行回收器3、CMS回收器4、G1回收器。串行回收器每次垃圾回收時(shí)串行回收器都只有一個(gè)工作線程。對(duì)于并行能力差的機(jī)器使用串行回收器反而性能更高、效率更好。因?yàn)闄C(jī)器并行能力差啟動(dòng)多個(gè)線程并行回收反而多了線程切換的開(kāi)銷。串行回收器是獨(dú)占式的垃圾回收過(guò)程中所有其他線程都要停止工作。串行回收器既可以用于新生代也可以用于老年代。新生代串行回收器新生代串行回收器使用復(fù)制算法容易實(shí)現(xiàn)、邏輯處理高效且沒(méi)有線程切換的開(kāi)銷。虛擬機(jī)以Client模式啟動(dòng)時(shí)串行回收器是默認(rèn)回收器。老年代串行回收器老年代串行回收器使用標(biāo)記壓縮算法。可以使用以下三種參數(shù)指明老年代使用串行回收器1、-XX:UseSerialGC參數(shù)新生代、老年代都使用串行回收器2、-XX:UseParNewGC參數(shù)JDK9和JDK10中該參數(shù)已作廢新生代使用ParNew回收器老年代使用串行回收器3、-XX:UseParallelGC參數(shù)指定新生代使用ParallelGC回收器老年代使用串行回收器。并行回收器并行回收器使用多個(gè)線程同時(shí)進(jìn)行垃圾回收對(duì)于并行能力更強(qiáng)的機(jī)型來(lái)說(shuō)可以有效減少每次垃圾回收所需的時(shí)間。新生代并行回收器ParNewParNew回收器是新生代垃圾回收器使用復(fù)制算法它只是簡(jiǎn)單地將新生代串行回收器多線程化。ParNew回收器也是獨(dú)占式的當(dāng)它啟動(dòng)時(shí)其他所有線程都要暫停。-XX:UseConcMarkSweepGCJDK9、JDK10中已不建議再使用該參數(shù)新生代使用ParNew回收器老年代使用CMS回收器。ParNew回收器的線程數(shù)可以通過(guò)-XX:ParallelGCThreads參數(shù)指定。該回收器的線程數(shù)一般需要與計(jì)算機(jī)的CPU核數(shù)關(guān)聯(lián)一般情況下1、當(dāng)CPU核數(shù)小于8時(shí)ParallelGCThreads的值取CPU的核數(shù)2、當(dāng)CPU核數(shù)大于8時(shí)ParallelGCThreads的值等于3(5*CPU_count/8)。新生代并行回收器ParallelGCParallelGC回收器也使用復(fù)制算法。看上去ParallelGC回收器和ParNew回收器很相似。不同的是ParallelGC回收器非常關(guān)注系統(tǒng)的吞吐量。新生代ParallelGC回收器可以使用兩種參數(shù)啟用1、-XX:UseParallelGC新生代使用ParallelGC回收器老年代使用串行回收器。2、-XX:UseParallelOldGC新生代使用ParallelGC回收器老年代使用ParallelOldGC回收器。ParallelGC回收器提供了兩個(gè)參數(shù)用于控制系統(tǒng)吞吐量1、-XX:MaxGCPauseMillis設(shè)置最大停頓時(shí)間該參數(shù)值是一個(gè)大于0的整數(shù)。指定該參數(shù)后回收器會(huì)自動(dòng)調(diào)整Java堆的大小或其他系統(tǒng)參數(shù)盡量把停頓時(shí)間控制在MaxGCPauseMillis以內(nèi)。MaxGCPauseMillis不能設(shè)置得過(guò)小因?yàn)檫^(guò)小的話回收器會(huì)把堆調(diào)整得很小這樣會(huì)造成垃圾回收頻繁系統(tǒng)吞吐量反而更低了。2、-XX:GCTimeRatio設(shè)置吞吐量大小該參數(shù)值是一個(gè)0到100以內(nèi)的整數(shù)。系統(tǒng)將花費(fèi)不超過(guò)1/(1GCTimeRatio)的時(shí)間進(jìn)行垃圾回收。默認(rèn)GCTimeRatio到取值為99即有不超過(guò)1/(199)1%的時(shí)間用于垃圾回收。-XX:MaxGCPauseMillis參數(shù)和-XX:GCTimeRatio參數(shù)是互相矛盾的因?yàn)槿绻麥p小每次垃圾回收的時(shí)間吞吐量也會(huì)降低而增加吞吐量又會(huì)延長(zhǎng)每次垃圾回收的時(shí)間。ParallelGC回收器還可以通過(guò)-XX:UseAdaptiveSizePolicy參數(shù)打開(kāi)自適應(yīng)GC策略讓虛擬機(jī)自行完成GC調(diào)優(yōu)工作。老年代并行回收器ParallelOldGCParallelOldGC回收器使用標(biāo)記壓縮算法。ParallelOldGC回收器也是多線程的和新生代ParallelGC回收器一樣它也關(guān)注吞吐量。CMS回收器Concurrent Mark Sweep、并發(fā)標(biāo)記清除CMS回收器使用標(biāo)記清除算法。CMS回收器主要關(guān)注系統(tǒng)停頓時(shí)間。CMS回收器的工作過(guò)程CMS回收器工作時(shí)的主要步驟有1、初始標(biāo)記獨(dú)占式2、并發(fā)標(biāo)記3、預(yù)清理4、重新標(biāo)記獨(dú)占式5、并發(fā)清除6、并發(fā)重置。啟用CMS回收器使用參數(shù)-XX:UseConcMarkSweepGC可以聲明啟用CMS回收器。并行和并發(fā)的區(qū)別并發(fā)是指回收器和應(yīng)用線程交替執(zhí)行并行是指應(yīng)用程序完全掛起同時(shí)由多個(gè)線程一起執(zhí)行GC。CMS回收器的執(zhí)行CMS回收器不是獨(dú)占式的在CMS回收過(guò)程中應(yīng)用線程任然可以繼續(xù)執(zhí)行。應(yīng)用線程執(zhí)行過(guò)程中又會(huì)產(chǎn)生垃圾對(duì)象這些新的垃圾對(duì)象在當(dāng)前的CMS回收過(guò)程中是無(wú)法清理的。所以在CMS回收過(guò)程中要確保應(yīng)用線程有足夠的內(nèi)存可用。因此CMS回收器不會(huì)等到內(nèi)存耗盡的時(shí)候才進(jìn)行垃圾回收而是在內(nèi)存使用率到達(dá)一定程度時(shí)就開(kāi)始垃圾回收了。可以通過(guò)參數(shù)-XX:CMSInitiatingOccupancyFraction指定一個(gè)閾值默認(rèn)68即內(nèi)存使用率到達(dá)68%時(shí)CMS回收器就會(huì)執(zhí)行。CMS閾值的設(shè)置原則如果內(nèi)存使用率飛速增長(zhǎng)在CMS回收過(guò)程中已經(jīng)出現(xiàn)了內(nèi)存不足的狀況CMS回收就會(huì)失敗。CMS失敗后虛擬機(jī)會(huì)啟動(dòng)老年代串行回收器進(jìn)行垃圾回收這時(shí)應(yīng)用線程會(huì)被全部掛起造成很長(zhǎng)的停頓時(shí)間。如果內(nèi)存使用率上升緩慢可以將CMSInitiatingOccupancyFraction設(shè)大點(diǎn)以有效降低CMS的觸發(fā)頻率減少老年代回收次數(shù)。如果內(nèi)存使用率上升飛快可以將CMSInitiatingOccupancyFraction設(shè)小點(diǎn)以避免老年代串行回收器的觸發(fā)頻率。CMS內(nèi)存壓縮參數(shù)CMS回收器使用的是標(biāo)記清除算法這種算法容易產(chǎn)生內(nèi)存碎片。為了減少碎片CMS回收器提供了兩個(gè)用于壓縮內(nèi)存的參數(shù)1、-XX:UseCMSCompactAtFullCollectionCMS垃圾收集完成后進(jìn)行一次碎片整理非并發(fā)。2、-XX:CMSFullGCBeforeCompaction設(shè)定進(jìn)行了多少次CMS回收后進(jìn)行一次內(nèi)存壓縮。G1回收器Garbage FirstG1回收器是JDK1.7中引入的可以替代CMS回收器。G1仍舊屬于分代回收器。從堆的結(jié)構(gòu)上看G1回收器采用了分區(qū)算法新生代和老年代都不必連續(xù)。G1兼顧新生代和老年代其它回收器要么只工作在新生代要么只工作在老年代。G1每次回收都會(huì)進(jìn)行有效的內(nèi)存復(fù)制減少內(nèi)存碎片。由于分區(qū)G1可以每次只回收部分區(qū)塊縮小了回收范圍可以有效控制GC停頓。G1相關(guān)參數(shù)-XX:UseG1GC該參數(shù)用于聲明使用G1回收器。-XX:MaxGCPauseMillis該參數(shù)用于指定最大停頓時(shí)間。在實(shí)際運(yùn)行過(guò)程中如果任何一次停頓超過(guò)MaxGCPauseMillisG1就會(huì)調(diào)整新生代和老年代的比例、調(diào)整堆大小、調(diào)整晉升年齡等以符合預(yù)設(shè)目標(biāo)。-XX:InitiatingHeapOccupancyPercent這個(gè)參數(shù)用于指定當(dāng)整個(gè)堆使用率達(dá)到多少時(shí)觸發(fā)并發(fā)標(biāo)記周期。InitiatingHeapOccupancyPercent默認(rèn)值是45即當(dāng)整個(gè)堆的使用率達(dá)到45%時(shí)觸發(fā)并發(fā)標(biāo)記周期。InitiatingHeapOccupancyPercent一旦被設(shè)置G1就不會(huì)再修改它了。禁用System.gc()默認(rèn)情況下System.gc()會(huì)顯式觸發(fā)Full GC同時(shí)對(duì)老年代和新生代進(jìn)行回收。使用-XX:-DisableExplicitGC可以禁用顯式GC它使System.gc()等價(jià)于一個(gè)空函數(shù)調(diào)用。調(diào)用System.gc()時(shí)使用并發(fā)回收默認(rèn)情況下如果沒(méi)有禁用System.gc()會(huì)使用傳統(tǒng)的Full GC方式回收整個(gè)堆。使用參數(shù)-XX:ExplicitGCInvokesConcurrent可以使用并發(fā)方式進(jìn)行回收。不使用這個(gè)參數(shù)即使啟用了CMS或G1也不會(huì)進(jìn)行并發(fā)回收。TLAB線程本地分配緩存Thread Local Allocation BufferTLAB是線程專屬的內(nèi)存分配區(qū)域。在同一時(shí)刻通常會(huì)有大量的線程在對(duì)上申請(qǐng)內(nèi)存空間所以每一次內(nèi)存分配都必須進(jìn)行加鎖同步加鎖是效率低下的。TLAB由于是線程專屬的其他線程無(wú)法訪問(wèn)所以能夠不用加鎖就進(jìn)行內(nèi)存分配。TLAB在eden區(qū)。青蛙客服系統(tǒng)https://download.csdn.net/download/look4liming/93326820