JVM經常使用的回收算法是:
標記/清除算法
標記/複製算法
標記/整理算法
其中上訴三種算法都先具有,標記階段,經過標記階段,獲得當前存活的對象,而後再將非標記的對象進行清除,而對象內存中對象的標記過程,則是使用的
「根搜索算法」,經過遍歷整個堆中的GC ROOTS,將全部可到達的對象標記爲存活的對象的一種方式,則是 「根搜索算法」,其中根是指的「GC ROOTS」,在JAVA中,充當GC ROOTS的對象分別有:「虛擬機棧中的引用對象」,「方法區中的類靜態屬性引用的對象」,「方法區中的常量引用對象」,「本地方法棧中JNI的引用對象」,凡是於上述對象存在可到達對象時,則該對象將標記爲可存活對象,不然則爲不可達對象,便可回收對象。在根搜索算法以前,還存在一個「引用計數算法」,即根據對象的被引用次數來進行計算,凡是對象的引用次數爲0時,則表示爲可回收對象,但對於互相引用的對象,若是不手動將互相引用的對象置空時,則該對象的引用次數永遠將不會爲0,則永久不會回收,則必然是錯誤,可參考:https://www.cnblogs.com/zuoxiaolong/p/jvm3.html
根據上述所提到的算法,可知:「根搜索算法」解決了標記那些對象是可回收,那些是不可回收對象的一個做用,可是對於具體在標記後的,回收行爲,則是上述前三個算法的具體應用了,分別是「標記後清除」,以及「標記後複製」,及「標記後整理」,
「標記清除算法」的優點以及劣勢:因爲標記清除算法只須要兩種動做行爲,分別是:1.經過根搜索算法,標記可到達的存活對象,2.清除不可到達的對象內存;經過使用前面的兩步,則將對應的不可達內存對象,進行了快速的清理,可是對於被回收後剩餘的空閒內存的空間則是不連續的,由於被回收對象都是隨機存在於內存的各個角落,在被回收後,內存的空間天然是存活的對象各自佔據在各自原有的對象內存位置中,而並無將剩餘的存活對象進行相關的內存空間的整理,因此對於後續 分配數組對象時,尋找一個連續的內存空間則是一個較爲麻煩的事情,。故:"標記/清除"的優點則是,內存空間的整理快速,效率較高,但劣勢則是:對應的清理後空間則是不連續的內存空間。
「標記/複製算法」:經過維護一份空閒內存的方式,來進行對象的回收,如:當前內存分爲兩份,分別爲活動內存T1和非活動內存T2,在使用中時使用活動內存,當活動內存滿的時候,進行 對象的標記,獲得當前的存活對象,此時將對應的存活對象,複製到對應的非活動內存T2當中,且嚴格按照對象內存地址進行依次排列,與此同時,GC線程將更新存活對象的內存引用地址指向新的內存地址; 對象複製的同時T1內存中的對象所有進行清除,此時的T2則扮演着活動內存的角色,而T1則是非活動內存,經過上述可知,使用複製算法的方式避免了「標記/清除」算法對於空間連續性的弊端,但複製算法的劣勢則是,一直保留着一份空閒的內存,做爲對應的備用內存,這整整浪費了一半的內存,相對來時仍是比較浪費的。
「標記/整理算法」:1. 標記:它的第一個階段與標記/清除算法是如出一轍的,均是遍歷GC Roots,而後將存活的對象標記。2. 移動全部存活的對象,且按照內存地址次序依次排列,而後將末端內存地址之後的內存所有回收。所以,第二階段才稱爲整理階段。 能夠看出標記整理算法,是經過標記全部的存活對象,而後再嚴格按照內存地址移動對應的存活對象後,再將末端的內存地址所有回收的方式,來進行的內存的空間整理,,因此,標記整理算法,並不是是單單的:標記/清除/整理的方式,而是經過整理存活對象的連續性地址後,再進行末端地址回收的方式進行的內存的整理。;經過上述也能夠看出 標記整理算法,彌補了標記清除對於不連續空間的內存整理的特性,也避免了複製算法對於一半空閒內存的浪費的特性。儘管標記整理擁有較好的特性,但沒有特別完美的算法,因此,在劣勢上:標記整理算法的總體執行效率是要低於標記複製算法的。
此處想要說明一下:JVM對於內存的清理上來看:標記整理算法,分別是先經過掃描GC ROOT獲得存活對象,而後 移動對應的存活對象的地址,使其進行以此排列,而後將 依次進行排列的內存地址,日後的全部的末端內存,直接進行回收 的方式來進行具體的操做的。因此,「標記整理算法」實際上的操做方式能夠分爲三步,分別是:1. 標記 2. 移動對象所在內存地址,3. 將末尾內存直接所有清除。而,「複製算法」則是:1. 標記存活對象,2. 移動存活對象所在地址,將其移動到空閒內存中便可。相同操做的狀況下,能夠看出:複製算法的效率是大於標記整理算法的。畢竟整理算法除了和複製算法都操做了具體的內存地址的移動之外,還比複製算法多出了一個末尾清除的步驟,因此:複製算法的效率>整理算法,,而「標記清除算法」,1. 標記全部存活對象,2. 清除全部「不連續的」空間內存。經過對比一些時間複雜度和執行效率上來看,JVM對於不連續的內存空間的清理的執行時間,彷佛是要大於整理算法直接將末尾內存直接清除的執行時間的,因此簡單的去看執行效率和時間複雜度上來看:標記複製算法>標記整理算法>標記清除算法(也多是因爲標記清除算法是比較老的算法的緣故,致使標記清除算法的執行效率對於其他的兩種算法,但實際狀況則不見得必定是這樣,此處的效率只是簡單的對比了時間複雜度來看,實際狀況lz總仍是以爲標記清除算法的執行時間和效率是大於整理算法的,畢竟單單從執行步驟來看,標記清除算法的執行是佔據優點的,除非jvm對於非連續內存的清除方式真的是過於較低而致使,此處先作一下簡單的記錄罷了)
最終的算法,分代收集算法,經過將jvm的內存區域進行劃分所進行執行的一直算法方式:
JVM中運行時內存區域分別有:堆,棧,本地棧,方法區,寄存器;其中棧和寄存器指針,是線程執行時的私有內存,線程結束後則棧內存同步釋放, 因此JVM的內存回收,則共需關注的是堆以及方法區的內存回收,其中堆是各個對象建立時的內存區域,而方法區則包含類的calss以及常量,靜態資源所對應的各個內存的存儲區域。因此集中在堆中的不存活對象以及方法區的對象的回收,即是總體GC內存回收時的重點,;
「分代收集算法」:JVM中將堆劃分爲不一樣的區域,分別是 新生代,老年代,以及永久代,根據對象的聲明週期不一樣,因此針對不一樣生命週期的對象的回收方式也不一樣,以此來增長回收效率。
在Java堆中:大多數對象是在新生代中被建立,當新生代中的對象在經歷過屢次Minor GC後,且仍然爲存活對象的數據,則將會晉升到老年代,(其中包含了晉升閥值和JVM自動調節晉升閥值的一個概念),當簡單了接了上述概念後,則已經基本了接了新生代以及老年代的做用,下面詳細進行下相關的介紹:
在Java中,新建立的對象數據則都是在新生代中進行建立,通常表現特徵爲生命週期較短,經過新生代的垃圾回收後只要少許的對象存活,因此新生代更加適合 執行效率較高的複製算法,針對複製算法的執行特徵,因此要存在一份備用的內存區域來做爲新生代在內存回收後的臨時對象的存儲場地,因而 新生代中便又劃分爲了對應的內存區域分別爲:Eden區,以及 兩個 Survivor區域,其中Eden區域的內存特徵和新生代最初的內存特徵不變,是用於存放對象在初始建立時的內存區域,當Eden區中的新生代對象佔滿了對應的Eden區域內存空間時,便會發生對應的Minor GC,即對應的內存回收,因爲Eden 區域中的對象生命週期廣泛較短,在經歷第一次的Minor GC後,則將對應的存活對象,移動到對應的Survivor區域,其中兩個Survivor區域中,選擇任意一個,做爲存活對象的新的存儲空間,因此此處由此可知,Survivor做爲Eden GC後的備用倉庫,Survivor的大小設置只須要能夠存儲下Eden區的存活對象便可,通常推薦,Survivor區域的內存大小佔整個年輕代的1/6便可,即:-XX:SuvrivorRatio=4,固然,全部的內存值的設置,均可以在後續根據項目的具體狀況進行對應的GC的優化,當第一個from Survivor區域空間滿時,則將會把對應的對象轉移到對應的to Survivor中,而後清空對應的from Survivor區域,而後依次進行復制算法的循環,在對象不斷的從From Suvrivor轉移到to Survivor以及從to Survivor轉移到from Suivivor的同時,Survivor的做用除了是Eden區的備用倉庫外,還具有篩選「老對象」的做用,當Survivor中的對象在經歷過屢次的Minor GC時,尚未被清除時,則即可以晉升爲「老年代」,老年代通常用於存儲存活時間更長的對象數據,而如何識別對象具有晉升爲「老年代」的數值,則能夠經過MaxTenuringThrehold進行設置,默認閥值爲15,即年輕代中的對象在經歷過15次的Minor GC還存在於對象空間的數據,則能夠晉升到年老代,,,,但:若是年輕代的對象數據不斷增加,而Survivor區域的對象還遲遲不知足MaxTenuringThrehold所設置的晉升閥值,此時一旦Survivor內存溢出,則不管對象的年齡閥值是多大,則都會所有晉升到年老代中,這對於年老代來講是個噩夢,由於這將致使不斷的Full GC,且會不斷下降程序的執行性能,,,,因此爲了避免存在MaxTenuringThrehold設置過大,而致使的晉升失敗的狀況,JVM則引入了動態的年齡計算,當累計的某個年齡大小的對象,超過了Survivor的一半時,則取當前的對象年齡做爲新的對象晉升閥值,可參考:https://mp.weixin.qq.com/s/t1Cx1n6irN1RWG8HQyHU2w
上面簡單介紹了下相關的年輕代的回收的一些知識和問題後,後面陸續再分析下當前經常使用的GC的收集器分別有哪些:,以及各收集器的做用和各個參數的調節及注意事項等。
關於GC收集器的更多簡介能夠參考該連接:https://www.cnblogs.com/zuoxiaolong/p/jvm8.html
首先;經常使用的收集器分別是:串行,並行,併發 收集器,其中串行通常用於Cliend模式,即當前代碼開發過程調試過程時所設置的模式,串行收集器分別包含:Serial Garbage Collector 串行年輕代收集器(複製算法),和 Serial Old Garbage Collector 串行老年代收集器(標記/整理算法),
而並行收集器:則包括:ParNew Garbage Collector ,Paraller Scavenge,這兩個是專門爲年輕代設計的並行收集器,皆爲複製算法,其中Paraller Scavenge則是-Server模式下的默認年輕代收集器,除此以外,並行收集器還剩餘:Paraller Old,此收集器是老年代的並行收集器爲 標記/整理算法,也是-Server模式下的默認老年代收集器。
惟一的一個併發收集器:是專門用於年老代回收時的併發收集器:concurrent mark sweep(簡稱CMS),真正作到了GC程序和應用程序併發執行,不會暫停應用的執行程序的一款收集器。(所使用的執行算法爲:標記/清除算法)。
---------------------------- 注:全部的GC收集器在執行過程中,都會暫停應用線程,只是通常年輕代使用並行收集器的GC,因爲並行執行,則應用的停頓時間則相對較短,因此感覺不到對應的應用暫停的特徵,但其實的確是先暫停對應的應用線程在GC執行事後,再喚醒對應的應用線程繼續執行,能夠經過查看GC日誌,來查看當前GC時的實際耗費時間,。,,,,而CMS則是惟一一個,在GC收集時和應用程序線程同步進行的一款收集器,只是只適用於年老代的併發收集,,因此合適的收集器的組合,才能夠出現更優的效果;,而且,在HotSport 中,除了CMS以外,其餘的老年代收集器,在執行的過程當中,都會同時收集整個GC堆,包括新生代,(此處是須要注意的)。
合適的收集器的選擇:
對於對響應時間有較高的要求的系統,能夠選擇ParNew 做爲新生代並行收集器,& CMS 做爲對應的老年代收集器, 因爲ParNew是並行收集,所以新生代的GC速度會很是快,停頓時間很短。而年老代的GC採用併發蒐集,大部分垃圾蒐集的時間裏,GC線程都是與應用程序併發執行的,所以形成的停頓時間依然很短。
對於對系統吞吐量有要求的系統,可選擇Paraller Scavenge做爲 年輕代的並行收集器,使用Paraller Old 做爲年老代的並行收集器,因爲年輕代和年老代都是使用並行收集器,因此對系統停頓時間較短,且Paraller Scavenge收集器能夠更加精準的控制GC的停頓時間和吞吐量的設置,因此對於在單位時間對系統可完成的指令數(吞吐量)有要求,可是對系統的響應時間沒有過大要求的系統可使用上述的兩種結合處理器;(要想在單位時間內處理的請求更多,即系統的吞吐量更高,則設置相關的年輕代的大小,能夠有效的增長系統的吞吐量和處理時間。)
JVM的可參考配置:
ParNew & CMS:
Paraller Scavenge & Paraller Old:
假設當前項目所部屬服務器內存爲8G,且總活躍對象數據爲1G(1G的活躍對象數據已經很大了),
則當前總堆的對象數據設置爲:(初始jvm默認內存設置與 總的JVM堆的可分配內存Xmx一致,可避免JVM GC後堆的從新分配);
堆的大小設置:-Xms8192m,-Xmx8192m
新生代的設置比例爲:-Xmn1536m,
老年代的設置比例爲:-XX:NewRatio用於設置年輕代與年老代所佔比例值,當上述新生代採用Xmn進行設置時,此處NewRatio能夠不用設置,則默認爲 總堆內存-新生代內存 = 老年代內存;
永久代設置比例爲:-XX:PermSize=1536m,-XX:MaxPermSize=1536m
新生代中Eden與from to內存區的比例:-XX:SurvivorRatio=4,表示當前Eden區和兩個From區的比值爲:4:2,則當前eden區佔整個年輕代的4/6;(說明一下:JVM中的動態年齡計算,則是根據對應的From區的大小和對象的年齡進行閥值的計算的)
新生代對象晉升年齡閥值的設置:-XX:MaxTenuringThreshold=15,默認狀況下對象的晉升年齡閥值爲15,上面已經提到過了JVM則會根據新生代中倖存區Survivor(及From 和to區域)的大小以及倖存區中對象的年齡動態計算晉升閥值的數值,那麼?是否是此處設置XX:MaxTenuringThreshold則無效了嗎?錯!,JVM在設置晉升閥值是根據所計算出的年齡值和XX:MaxTenuringThreshold的年齡值進行對比,那個值越小,則使用當前更小的年齡值做爲新的晉升閥值,因此若是設置XX:MaxTenuringThreshold的值爲0,或者更小的值1,2,等,則將更快的增長新生代進入老年代的頻率,(舉個例子,對於年老代比較多的應用能夠直接將對象晉升到年老代,且因爲CMS的年老代回收是隻回收年老代且併發收集過程當中不影響應用線程的運行,因此直接晉升年老代,對於GC的回收的時間和效率彷佛也是個不錯的選擇,不過目前沒有遇到過這種相似的狀況的應用),
單個線程所佔用堆棧內存的大小設置:-Xss256k,用於表示當前每一個線程在建立時所對應的線程棧的空間內存的大小,具體可詳細看下 (書寫)java虛擬機的內存區域分配(一個不斷記錄和推翻以及再記錄的一個過程)該篇記錄文章,此處主要是說明下Xss建立線程時所佔用的內存爲操做系統的內存,而並不是堆的內存空間,因此在設置堆的內存大小時,也須要留存下對應的操做系統內存,處理分配給對應的操做系統的應用使用外,還應留存對應的線程內存,也須要設置對應的服務器當前應用進程可建立最大線程的參數設置,可參考所書寫的該篇文章; (*******書寫+csdn)由多線程內存溢出產生的實戰分析 - CSDN博客
以上,基本是一個默認在不配置GC收集器前對堆內存空間比例的基本設置,須要了接的是關於新生代中Eden和Suivivo的比例設置,在不進行配置的狀況下JVM實際也是會給一個默認的自動參數配置的,而且以上關於JVM配置的比例參數皆是設置對JDK1.6的比例設置,因此對於永久代的設置並不是是佔用的整個堆的內存比例進行設置的,而是使用的操做系統的內存進行的相關永久代內存的設置,------
---- 設置對應的GC收集器的參數配置:
---- ParNewGC + CMS 收集器的配置
-XX:+UseParNewGC -XX:ParallelGCThreads=4
設置開啓ParNew收集器 設置當前並行收集器開啓的線程數量:,參考對應的url(http://www.blogjava.net/paulwong/archive/2014/06/16/414812.html)
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSScavengeBeforeRemark
設置當前CMS後執行碎片整理 設置多少次FULL GC後進行碎片整理 表示老年代內存達到使用率的80%時,則進行CMS GC 設置每次CMS GC的Remark前執行下Minor GC
(上述4個參數可參考URL:http://www.cnblogs.com/onmyway20xx/p/6605324.html)
-XX:+UseConcMarkSweepGC -XX:ParallelCMSThreads=4 -XX:MaxDirectMemorySize=256M -XX:+CMSParallelRemarkEnabled
設置開啓CMS 收集器 CMS默認開啓線程數設置(默認爲(ParallelGCThreads+3)/4) 堆外內存的大小設置(如ByteBuffer字節緩存時,則佔用的爲堆外內存:查看連接:http://hellojava.info/?tag=maxdirectmemorysize ,https://blog.csdn.net/aesop_wubo/article/details/38406709) ----- CMS在Remark執行前,會執行一個可中斷的併發預清理(CMS-concurrent-abortable-preclean),默認爲開啓CMSParallelRemarkEnable,此處使用-XX:+CMSParallelRemarkEnabled顯示開啓,(注:CMS的Remark階段是全量堆搜索(old+xmn)獲得存活對象,詳情可查看:美團文章的案例二:https://mp.weixin.qq.com/s/t1Cx1n6irN1RWG8HQyHU2w)
此處設置-XX:CMSInitiatingOccupancyFraction=80 的緣由爲:1. 每次CMS GC後,都設置了清理內存碎片,CMSFullGCsBeforeCompaction=0,因此不存在 晉升對象沒有連續的內存空間存儲而引發的CMS GC併發清理失敗的問題(CMS清理失敗將會使用serial old來進行STW全局的FULL GC),2.每次CMS執行Remark階段時已經提早執行了Minor GC,因此新生代空間滿了之後的再次晉升通常不會特別快,第二,Remark階段時的Minor GC,儘管可能會存在新生代的對象的晉升,但老年代剩餘的20%比例,應該是足足能夠存放下的,因此設置80%時觸發CMS的GC通常是OK的,固然80%的該值,也能夠經過每次Minor GC晉升對象的大小取其平均值獲得對應的大小,而後留下相對較爲充足的空間比例也是合適的,。
另:關於JVM什麼時候會觸發STW的Full GC的機制能夠查看:美團文章的第三個案例(https://mp.weixin.qq.com/s/t1Cx1n6irN1RWG8HQyHU2w)
另外:Full GC 和CMS的GC是不一樣的,CMS 的GC是單純的老年代GC,在GC日誌中對應的標識爲:CMS-inital-mak,CMS-concurrent-mark-start,等CMS的日誌標識,
能夠查看該圖片(https://mmbiz.qpic.cn/mmbiz_png/hEx03cFgUsV0a8RiaD6p2fV75LI7IdZVMia9ML0LccOY9bWeBvXzMbQnWaKmyhmWXDO1eU4U5X4YrvJdZpPE1pbQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1),除了CMS的GC外,其餘的老年代GC在執行時好像是都同時執行的FULL GC,上面也有提到過,關於CMS執行時的四個階段的特性和執行方式,也能夠查看美團的文章的案例2,以及該文章中對GC收集器的介紹,其中包含CMS的收集器的簡單介紹(https://www.cnblogs.com/zuoxiaolong/p/jvm8.html)
-XX:+PrintTenuringDistribution 開啓jvm對象晉升年齡的打印 Desired survivor size 107347968 bytes, new threshold 1 (max 30)
可參考推薦GC爲:
-Xmn512M -Xms1024M -Xmx1024M -XX:MaxPermSize=250M -Xss256k -Xconcurrentio -XX:SurvivorRatio=4 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=15 -XX:MaxDirectMemorySize=256M -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSScavengeBeforeRemark -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider -Dsun.net.inetaddr.ttl=60 -Dorg.mortbay.jetty.Request.maxFormContentSize=-1 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/ftdq_kbase/ibot_core_8013/logs -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/app/ftdq_kbase/ibot_core_8013/logs/gc.log
-Xmn512M -Xms1024M -Xmx1024M -XX:MaxPermSize=250M -Xss256k -Xconcurrentio -XX:SurvivorRatio=4 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=15 -XX:MaxDirectMemorySize=256M -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSScavengeBeforeRemark -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider -Dsun.net.inetaddr.ttl=60 -Dorg.mortbay.jetty.Request.maxFormContentSize=-1 www.dfgjpt.com-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/ftdq_kbase/ibot_core_8013/logs www.thd178.com/ -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/app/ftdq_kbase/ibot_core_8013/logs/gc.log -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=22223
JAVA_OPTIONS="-Xmn1000M -Xms3000M -Xmx3000M -XX:MaxPermSize=512M -Xss128k -Xconcurrentio -XX:SurvivorRatio=5 -XX:TargetSurvivorRatio=90 -XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSParallelRemarkEnabled -XX:+CMSPermGenSweepingEnabled -XX:MaxTenuringThreshold=31 -XX:CMSInitiatingOccupancyFraction=90 -Xloggc:/opt/jetty_kbase-search-7661/logs/gc.log -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses www.tiaotiaoylzc.com-XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseConcMarkSweepGC -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider www.dfgjpt.com -Dsun.net.inetaddr.ttl=60 -Dorg.mortbay.jetty.Request.maxFormContentSize=-1 -Djava.rmi.server.hostname=172.16.9.55 -Dcom.sun.management.jmxremote.port=17661 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dsolr.solr.home=/opt/jetty_kbase-search-7661/solr_home/solr www.yongshi123.cn www.mhylpt.com -Dsolr.library.home=/opt/jetty_kbase-search-7661/solr_home"
此處須要注意一點,第三個參數中爲search參數,新增了 +ExplicitGCInvokesConcurrentAndUnloadsClasses 標識,(參考:https://blog.csdn.net/aesop_wubo/article/details/38406709)
因爲系統中使用-XX:MaxDirectMemorySize=256M(上面提到的堆外內存的設置,用於文件讀取拷貝時增長效率),可是堆外內存的設置後,發現每隔一小時會進行一次Full GC,但此時FUll GC時,老年代根本沒有用滿,且永久代也沒有用滿(方太上即是這種狀況,也是一個小時觸發一次Full GC),根據上述的參考連接可知:觸發Full GC的目的主要是想要回收 堆外內存的回收,即Native所使用內存的回收,但Young GC時,不具有回收堆外內存的狀況,因此會主動觸發Full GC進行內存回收,此處使用 ExplicitGCInvokesConcurrentAndUnloadsClasses 參數,能夠將回收堆外內存的任務交給 CMS進行處理,CMS 的回收好處相比於Fulll GC的好處,此處則再也不作累贅,經過使用CMS回收堆外內存的狀況,則能夠避免頻繁的Full GC,(FUll GC 期間對系統是有影響的,且是STW的,因此對於使用CMS和Full GC進行回收堆外內存,此處也應該根據實際需調整,由於CMS 在併發預清理階段也是STW的,不過合理的配置CMS,則回收時間應該也是最佳的),另外,提一下上述的一個問題,上述的 -XX:+CMSParallelRemarkEnabled 表示在CMS,Remark以前,進行一個可中斷的併發預清理,(此處其實能夠不開啓,由於此處已經使用CMSScavengeBeforeRemark ,表示每次CMS前進行一次年輕代的回收,那麼 此時則沒有必要等待5秒或怎樣的一箇中斷的預清理了,此處作已備註,可考慮測試去除等操做)
併發收集的參數默認 -XX:UseAdaptiveSizePolicy的開啓,將會全權管理內存分配,此時所設置的新生代的eden和survivor的比例配置將會失效,等,。
CMS的設置,能夠設置FUll GC前先進行下相關的Minor GC的回收,以及能夠設置是否開啓對永久代的回收,(由於若是應用中存在較多的動態類,或使用String.inten()等將數據都放置到了對應的常量池中,則對永久代Perm的回收則也是有必要的, )除此以外,能夠參考美團,或者jvm參數設置中對CMS的一些配置的說明,也是較爲清晰和詳細的。
對象每經歷一次Minor GC,年齡加1,達到「晉升年齡閾值」後,被放到老年代,這個過程也稱爲「晉升」。顯然,「晉升年齡閾值」的大小直接影響着對象在新生代中的停留時間,在Serial和ParNew GC兩種回收器中,「晉升年齡閾值」經過參數MaxTenuringThreshold設定,默認值爲15。html