天天看點

JVM深入了解及性能調優jvm記憶體結構及性能調優

jvm記憶體結構及性能調優

一、jvm簡介

java虛拟機(java virtual machine jvm)一種能夠運作java位元組碼的虛拟機。java程式經過一次編譯之後,将java代碼編譯為位元組碼也就是class檔案,然後在不同的作業系統上依靠不同的java虛拟機進行解釋,最後再轉換為不同平台的機器碼,最終得到執行,這實作了java的跨平台機制。

三種JVM:① Sun公司的HotSpot ② BEA公司的JRockit ③ IBM公司的J9 JVM

二、jvm記憶體結構

jvm由三個子系統組成:

  1. 内加載子系統
  2. 運作時資料區(記憶體結構)
  3. 執行引擎

jvm記憶體結構:

JVM深入了解及性能調優jvm記憶體結構及性能調優

2.1 方法區(Method Area)

方法區用于存儲虛拟機加載的:靜态變量+常量+類資訊+運作時常量池 (類資訊:類的版本、字段、方法、接口、構造函數等描述資訊 )方法區是全局共享的,在一定條件下也會被GC。當方法區超過它允許的大小時,就會抛出OutOfMemory:PermGen Space異常。

在Hotspot虛拟機中,這塊區域對應持久代或永久代(Permanent Generation),一般來說,方法區上執行GC的情況很少,是以方法區被稱為持久代的原因之一,但這并不代表方法區上完全沒有GC,其上的GC主要針對常量池的回收和已加載類的解除安裝。在方法區上進行GC,條件相當苛刻而且困難。

運作時常量池(Runtime Constant Pool)是方法區的一部分,用于存儲編譯器生成的常量和引用。一般來說,常量的配置設定在編譯時就能确定,但也不全是,也可以存儲在運作時期産生的常量。比如String類的intern()方法,作用是String類維護了一個常量池,如果調用的字元”hello”已經在常量池中,則直接傳回常量池中的位址,否則建立一個常量加入池中,并傳回位址。

2.2 堆 (Heap)

堆區是GC最頻繁的,也是了解GC機制最重要的區域。堆區由所有線程共享,在虛拟機啟動時建立。堆區主要用于存放對象執行個體及數組,所有new出來的對象都存儲在該區域。

2.3 虛拟機棧(VM Stack)

虛拟機棧占用的是作業系統記憶體,每個線程對應一個虛拟機棧,它是線程私有的,生命周期和線程一樣,每個方法被執行時産生一個棧幀(Statck Frame),棧幀用于存儲局部變量表、動态連結、操作數和方法出口等資訊,當方法被調用時,棧幀入棧,當方法調用結束時,棧幀出棧。

局部變量表中存儲着方法相關的局部變量,包括各種基本資料類型及對象的引用位址等,是以他有個特點:記憶體空間可以在編譯期間就确定,運作時不再改變。

虛拟機棧定義了兩種異常類型:StackOverFlowError(棧溢出)和OutOfMemoryError(記憶體溢出)。如果線程調用的棧深度大于虛拟機允許的最大深度,則抛出StackOverFlowError;不過大多數虛拟機都允許動态擴充虛拟機棧的大小,是以線程可以一直申請棧,直到記憶體不足時,抛出OutOfMemoryError。

2.4 本地方法棧(Native Method Stack)

本地方法棧用于支援native方法的執行,存儲了每個native方法的執行狀态。本地方法棧和虛拟機棧他們的運作機制一緻,唯一的差別是,虛拟機棧執行Java方法,本地方法棧執行native方法。在很多虛拟機中(如Sun的JDK預設的HotSpot虛拟機),會将虛拟機棧和本地方法棧一起使用。

2.5 程式計數器(Program Counter Register)

程式計數器是一個很小的記憶體區域,不在RAM上,而是直接劃分在CPU上,程式猿無法操作它,它的作用是:JVM在解釋位元組碼(.class)檔案時,存儲目前線程執行的位元組碼行号(不是下一行),隻是一種概念模型,各種JVM所采用的方式不一樣。位元組碼解釋器工作時,就是通過改變程式計數器的值來取下一條要執行的指令,分支、循環、跳轉等基礎功能都是依賴此技術區完成的。

每個程式計數器隻能記錄一個線程的行号,是以它是線程私有的。

如果程式目前正在執行的是一個java方法,則程式計數器記錄的是正在執行的虛拟機位元組碼指令位址,如果執行的是native方法,則計數器的值為空,此記憶體區是唯一不會抛出OutOfMemoryError的區域。

運作時棧幀結構:

JVM深入了解及性能調優jvm記憶體結構及性能調優

棧幀是用于支援虛拟機進行方法調用和方法執行的資料結構,它是虛拟機運作時資料區中的虛拟機棧的棧元素。棧幀存儲了方法的局部變量表、操作數棧、動态連接配接和方法傳回位址等資訊。每一個方法從調用開始到執行完成的過程,就對應着一個棧幀在虛拟機棧裡面從入棧道出棧的過程。

在編譯程式代碼的時候,棧幀中需要多大的局部變量表、多深的操作數棧都已經完全确定了,并且寫入到方法的code屬性之中,是以一個棧幀需要配置設定多少記憶體,不會受到程式運作期變量資料的影響,而僅僅取決于具體的虛拟機實作。

(1)局部變量表

局部變量表是一組變量值存儲空間,用于存放方法參數和方法内部定義的局部變量。在Java程式被編譯為Class檔案時,就在方法的Code屬性的max_locals資料項中确定了該方法所需要配置設定的最大局部變量表的容量。

局部變量表的變量以變量槽(Slot)為最小機關,虛拟機規範中并沒有明确指明一個Slot應占用的記憶體空間大小。

虛拟機通過索引定位的方式使用局部變量表,索引值的範圍是從0開始到局部變量表最大的Slot數量。在方法執行時,虛拟機是使用局部變量表完成參數值到參數變量清單的傳遞過程的,如果是執行個體方法(非static方法),那麼局部變量表中第0位索引的Slot預設是用于傳遞方法所屬對象執行個體的引用,在方法中可以通過關鍵字“this”來通路這個隐含的參數。其餘參數則按照參數表的順序來排列,占用從1開始的局部變量Slot,參數表配置設定完畢後,再根據方法體内部定義的變量順序和作用域配置設定其餘的Slot。

局部變量表中的Slot是可重用的,方法體中定義的變量,其作用于并不一定會覆寫整個方法體,如果目前位元組碼PC計數器的值已經超出了某個變量的作用域,那麼這個變量對應的Slot就可以交給其他變量使用。

對于64位的資料類型,虛拟機會以高位在前的方式為其配置設定兩個連續的Slot空間。

(2)操作數棧

操作數棧也常被稱為操作棧,它是一個後入先出棧。同局部變量表一樣,操作數棧的最大深度也是在編譯的時候被寫入到Code屬性的max_stacks資料項中。32位資料類型所占的棧容量位1,64位資料類型所占的棧容量位2.

當一個方法剛剛開始執行的時候,這個方法的操作數棧是空的,在方法的執行過程中,會有各種位元組碼指令向操作數棧中寫入和提取内容,也就是入棧出棧操作。舉個例子:整數加法位元組碼指令iadd在運作的時候要求操作數棧中最接近棧頂的兩個元素已經存入了兩個int類型的數值,當執行這個指令時,會将這兩個int值出棧并相加,然後将相加的結果入棧。

操作數棧中元素的資料類型必須與位元組碼指令的序列嚴格比對,在編譯程式代碼的時候,編譯器要嚴格保證這一點,在類校驗階段的資料流分析中還要再次驗證這一點。

(3)動态連接配接

每個棧幀都包含一個指向運作時常量池中該棧幀所屬方法的引用,持有這個引用是為了支援方法調用過程中的動态連接配接。

(4)方法出口

當一個方法被執行後,有兩種方式退出這個方法。第一種方式是:執行引擎遇到任意一個方法傳回的位元組碼指令,這時候可能會有傳回值傳遞給上層的方法調用者(調用目前方法的方法稱為調用者),是否有傳回值和傳回值類型将根據遇到各種方法傳回指令來決定,這種退出方法的方式稱為正常完成出口。

另一種退出方式是,在方法執行過程中遇到了異常,并且這個異常沒有在方法體内得到處理,無論是Java虛拟機内部産生的異常,還是代碼中使用athrow位元組碼指令産生的異常,隻要在本方法的異常表中沒有搜尋到比對的異常處理器。就會導緻方法退出,這種退出方法的方式稱為異常完成出口。

無論采用何種退出方式,在方法退出後,都需要傳回到方法被調用的位置,程式才能繼續執行,方法傳回時可能需要在棧幀中儲存一些資訊,用來幫助恢複它的上層方法的執行狀态。一般來說,方法正常退出時,調用者的PC計數器的值就可以作為傳回位址,棧幀中很可能會儲存這個計數器值。而方法異常退出時,傳回位址是要通過異常處理器表來确定,棧幀中一般不會儲存這部分資訊。

方法退出的過程實際上等同于把目前棧幀出棧,是以退出時可能執行的操作有:回複上層方法的局部變量表和操作數棧,把傳回值(如果有的話)壓入調用者棧幀的操作數棧中,調整PC計數器的值以指向方法調用指令後面的一條指令等。

JVM深入了解及性能調優jvm記憶體結構及性能調優

Eden:尹甸區 , 新建立的對象

From,To:S0,S1區也稱為幸存區,是兩塊大小相等,并且可以互換角色的空間

幸存區包括S0、S1區,或者說from區、to區

再經過一次GC之後,對象從from區,到to區

再經過一次GC之後,對象從to區,到form區

再經過一次GC之後,對象從from區,到to區

如此反複……

直到對象的年齡到達15歲之後,就會被放到老年區

這也是,為什麼幸存區中的S0、S1區

兩個區大小相等,并且可以互換角色

Young Generation:新生代 發生的GC 稱為minor GC (young GC) ,

Old Generation: 老年代 發生的GC 稱為MajorGC(Full GC ) ,如發生GC後記憶體仍然不夠用将抛出 OutOfMemoryError異常

Meta Data:元空間 jdk1.8之前叫Permanent Generation 永久代 ,是非堆記憶體

三、垃圾回收常見算法及收集器

垃圾回收算法

1、引用計算法

引用計數是垃圾收集器中的早期政策。在這種方法中,堆中每個對象執行個體都有一個引用計數。當一個對象被建立時,就将該對象執行個體配置設定給一個變量,該變量計數設定為1。當任何其它變量被指派為這個對象的引用時,計數加1(a = b,則b引用的對象執行個體的計數器+1),但當一個對象執行個體的某個引用超過了生命周期或者被設定為一個新值時,對象執行個體的引用計數器減1。任何引用計數器為0的對象執行個體可以被當作垃圾收集。

優點:引用計數收集器可以很快的執行,交織在程式運作中。對程式需要不被長時間打斷的實時環境比較有利。

缺點:無法檢測出循環引用。如父對象有一個對子對象的引用,子對象反過來引用父對象。這樣,他們的引用計數永遠不可能為0。

2、複制算法

此算法把記憶體空間劃分為兩個相等的區域,每次隻使用一個區域。垃圾回收時,周遊目前使用區域,把正在使用的對象複制到另外一個區域。此算法每次隻處理正在使用的對象,是以複制成本小,同時複制過去以後還能進行相應的記憶體整理,不會出現“碎片”問題。當然缺點也很明顯需要兩倍記憶體空間。

JVM深入了解及性能調優jvm記憶體結構及性能調優

3、标記-清除法

此算法分為兩個階段,第一階段從引用根節點開始标記所有被引用的對象,第二階段周遊整個堆,把未标記的對象清除。此算法需要暫停整個應用,同時會産生記憶體碎片。

JVM深入了解及性能調優jvm記憶體結構及性能調優

4、标記-整理算法

此算法結合了标記-清除和複制兩個算法的優點,也分為兩個階段,第一階段從引用根節點開始标記所有被引用的對象,第二階段周遊整個堆,把未标記的對象清除并且把存活的對象“壓縮”到堆的其中一塊,按順序存放。

JVM深入了解及性能調優jvm記憶體結構及性能調優

5、分代收集算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根據對象存活的生命周期将記憶體劃分為若幹個不同的區域。一般情況下将堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)。老年代的特點是每次垃圾收集時隻有少量對象需要被回收,而新生代的特點是每次垃圾回收時都有大量的對象需要被回收,那麼就可以根據不同代的特點采取最适合的收集算法。是以年輕代以複制算法為主,老年代以标記整理算法為主。

JVM深入了解及性能調優jvm記憶體結構及性能調優

垃圾收集器

1、Serial收集器(複制算法) 串行

新生代單線程收集器,标記和清理都是單線程,優點是簡單高效,但是會暫停其他所有工作線程。是client級别預設的GC方式,可以通過-XX:+UseSerialGC來強制指定。

2、Serial Old收集器(标記-整理算法)

老年代單線程收集器,Serial收集器的老年代版本。

3、ParNew收集器(停止-複制算法) 并行

新生代收集器,可以認為是Serial收集器的多線程版本,在多核CPU環境下有着比Serial更好的表現。

4、Parallel Scavenge收集器(停止-複制算法)

并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般為99%, 吞吐量= 使用者線程時間/(使用者線程時間+GC線程時間)。适合背景應用等對互動相應要求不高的場景。是server級别預設采用的GC方式,可用-XX:+UseParallelGC來強制指定,用

-XX:ParallelGCThreads=4來指定線程數。

5、Parallel Old收集器(停止-複制算法)

Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量優先。

6、CMS(Concurrent Mark Sweep)收集器(标記-清理算法)

高并發、低停頓,追求最短GC回收停頓時間,cpu占用比較高,響應時間快,停頓時間短,多核cpu 追求高響應時間的選擇。

7、G1(Garbage-First)

在JDK 7u4版本之後釋出的垃圾收集器,并在jdk9中成為預設垃圾收集器,其特點是高效且并發的,不需要分代配合不同的垃圾收集器,因為G1中的垃圾收集區域是“分區”(Region)的。啟用G1

-XX:+UserG1GC

完整示例:

java -Xmx50m -Xms50m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar c:\javademos\demo\jfc\Java2D\Java2demo.jar
           

GC是什麼時候觸發的(面試最常見的問題之一)

由于對象進行了分代處理,是以垃圾回收區域、時間也不一樣。GC有兩種類型:Scavenge GC和Full GC。

Scavenge GC

  一般情況下,當新對象生成,并且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,并且把尚且存活的對象移動到Survivor區。然後整理Survivor的兩個區。這種方式的GC是對年輕代的Eden區進行,不會影響到年老代。因為大部分對象都是從Eden區開始的,同時Eden區不會配置設定的很大,是以Eden區的GC會頻繁進行。因而,一般在這裡需要使用速度快、效率高的算法,使Eden去能盡快空閑出來。

Full GC

  對整個堆進行整理,包括Young、Tenured和Perm。Full GC因為需要對整個堆進行回收,是以比Scavenge GC要慢,是以應該盡可能減少Full GC的次數。在對JVM調優的過程中,很大一部分工作就是對于Full GC的調節。有如下原因可能導緻Full GC:

a) 老年代(Tenured)被寫滿;

b) 持久代(Perm)被寫滿;

c) System.gc()被顯示調用;

d) 上一次GC之後Heap的各域配置設定政策動态變化;

四、jvm性能調優

1、jvm監控工具

a、jps

用來檢視基于HotSpot JVM裡面所有程序的具體狀态, 包括程序ID,程序啟動的路徑等等

JVM深入了解及性能調優jvm記憶體結構及性能調優

b、jstat

檢視classloader,compiler,gc相關資訊,實時監控資源和性能 。jstat工具特别強大,可以用來監視VM記憶體内的各種堆和非堆的大小及其記憶體使用量。

文法:

jstat -<option> [-t] [-h<lines>] <pid> [<interval> [<count>]]

Options — 通常使用 -gcutil 檢視gc情況

interval – 間隔時間,機關為秒或者毫秒

count — 列印次數,如果預設則列印無數次

示例:

JVM深入了解及性能調優jvm記憶體結構及性能調優

c、jconsole 可以監控Java應用程式(如jar應用、tomcat等),但被監視的應用程式必須和jconsole是用同一個使用者運作的。jvisualvm的使用和jconsole類似。

本地監控:

jconsole pid

遠端監控:

jconsole [ hostname:portNum ]

使用遠端監控需要配置jmx代理資訊,修改Tomcat的bin目錄下的catalina.bat。

set JAVA_OPTS= %JAVA_OPTS% -Djava.rmi.server.hostname=HostIP set

JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.port=8888 set

JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.ssl=false set

JAVA_OPTS= %JAVA_OPTS%

-Dcom.sun.management.jmxremote.authenticate=false

連接配接成功後可以在overview中檢視記憶體、線程、類及CPU使用情況:

JVM深入了解及性能調優jvm記憶體結構及性能調優

d、 jmap

列印java程序的堆記憶體資訊。

jmap -heap pid         檢視heap的概要資訊,GC使用的算法、heap的配置及wise heap的使用情況.
jmap -histo[:live] pid      檢視堆記憶體中的每個類的類名、執行個體數量、記憶體占用大小
jmap -dump:live, format=b, file=fileName pid       将記憶體使用情況導出到檔案中,再用jhat、MAT、VisualVM分析檢視,以便查找記憶體溢出原因
           
JVM深入了解及性能調優jvm記憶體結構及性能調優

e、jstack

jstack用于生成java虛拟機目前時刻的線程快照,主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導緻的長時間等待等。

文法:

jstack [ option ] pid
    -F 當 jstack [-l] pid 沒有響應的時候強制列印棧資訊
    -l 長清單. 列印關于鎖的附加資訊,例如屬于java.util.concurrent的ownable synchronizers清單.
           

f、VisualVM

VisualVM 是一個工具,它提供了一個可視界面,用于檢視 Java 虛拟機 (Java Virtual Machine, JVM) 上運作的基于 Java 技術的應用程式(Java 應用程式)的詳細資訊。

VisualVM 是java jdk自帶的最牛逼的調優工具了,最常使用的調優工具,幾乎涉及了jvm調優的方方面面。

啟動方式:直接在jdk/bin目錄下面輕按兩下jvisualvm.exe既可使用

JVM深入了解及性能調優jvm記憶體結構及性能調優

VisualVM可以根據需要安裝不同的插件,每個插件的關注點都不同,有的主要監控GC,有的主要監控記憶體,有的監控線程等。

這裡介紹下安裝Visual GC插件

  1. 首先下載下傳對應jdk版本的插件,https://visualvm.github.io/pluginscenters.html
  2. 找到對應自己jdk版本的更新位址
    JVM深入了解及性能調優jvm記憶體結構及性能調優
  3. 在VisualVM 主菜單中選擇工具 --》 插件–》設定 編輯插件位址, 将剛剛的位址複制上去,驗證之後會發現可用插件欄顯示了各自插件
    JVM深入了解及性能調優jvm記憶體結構及性能調優
  4. 選擇Visual GC 安裝即可
JVM深入了解及性能調優jvm記憶體結構及性能調優

5、安裝完成後須重新開機才能生效

JVM深入了解及性能調優jvm記憶體結構及性能調優

VisualVM更全面更直覺一些,另外VisualVM非常多的其它功能,可以分析dump的記憶體快照,dump出來的線程快照并且進行分析等,還有其它很多的插件大家可以去探索。

JVM深入了解及性能調優jvm記憶體結構及性能調優

2、JVM調優常用參數配置

堆配置

-Xms:初始堆大小

-Xmx:最大堆大小

-XX:NewSize=n:設定年輕代大小

-XX:NewRatio=n:設定年輕代和年老代的比值。如:為3表示年輕代和年老代比值為1:3,年輕代占整個年輕代年老代和的1/4

-XX:SurvivorRatio=n:年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如3表示Eden: 3 Survivor:2,一個Survivor區占整個年輕代的1/5

-XX:MaxPermSize=n:設定持久代大小

收集器設定

-XX:+UseSerialGC:設定串行收集器

-XX:+UseParallelGC:設定并行收集器

-XX:+UseParalledlOldGC:設定并行年老代收集器

-XX:+UseConcMarkSweepGC:設定并發收集器

-XX:+UseG1GC:設定G1并發收集器

垃圾回收統計資訊

列印GC回收的過程日志資訊

-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

3、回收器選擇

JVM給了三種選擇:串行收集器,并行收集器,并發收集器,但是串行收集器隻适用于小資料量的情況,一般不考慮使用了,是以這裡隻針對并行收集器和并發收集器。預設情況下,JDK5.0以前是使用的串行收集器,如果想使用其他收集器需要在啟動時加入相應的參數,JDK5.0以後,JVM會根據系統目前的配置進行判斷

吞吐量優先的并行收集器

并行收集器主要以到達一定的吞吐量為目标,适用于背景處理

java -Xmx3550m-Xms3550m-Xss128k-XX:+UseParallelGC

-XX:ParallelGCThreads=20

-XX:+UseParallelGC:選擇垃圾收集器為并行收集器。次配置僅對年輕代有效。即上述配置下,年輕代使用并行收集,而年老代仍舊使用串行收集。

-XX:PARALLELgcThreads=20:配置并行收集器的線程數,即:同時多少個線程一起進行垃圾回收。此值最好配置與處理器數目相同。

-XX:+UseParallelOldGC:配置年老代來及收集方式為并行收集,JDK6.0支援對年老代并行收集

-XX:MaxGCPauseMillis=100:設定每次年輕代垃圾回收的最長時間,如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值

-XX:+UseAdaptiveSizePolicy:設定此選項以後,并行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目标系統規定的最低響應時間或者收集頻率等,此值建議使用并行收集器時,一直打開

響應時間優先的并發收集器

并發收集器主要是保證系統的響應時間,減少垃圾收集時的停頓時間。适用于應用伺服器、電信領域等。

-XX:CMSFullGCsBeforeCompaction=5

-XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction:由于并發收集器不對記憶體空間進行壓縮、整理、是以運作一段時間以後會産生“碎片”,使得運作效率降低。此值設定運作多少次GC以後對記憶體空間進行壓縮、整理

-XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,但是可以消除碎片

調優總結

年輕代大小選擇

響應時間優先的應用:盡可能設定大,直到接近系統的最低響應時間限制(根據實際情況選擇)。在此種情況下,年輕代收集發生的頻率也是最小的。同時減少到達年老代的對象。

吞吐量優先的應用:盡可能的設定大,可能到達GB的程度,因為對響應時間沒有要求,垃圾收集可以并行進行,一般适合8核CPU以上應用。

老年代大小選擇

響應時間優先的應用:年老代使用并發收集器,是以其大小需要小心設定,一般要考慮并發會話率和會話持續時間等一些參數。如果堆設定小了,可能會造成記憶體碎片、高回收頻率以及應用暫停而使用傳統的标記清除方式;如果堆大了,則需要較長的收集時間。最優化的方案,一般需要參考一下資料獲得:

1、并發垃圾收集資訊

2、持久代并發收集次數

3、傳統GC資訊

4、花在年輕代和年老代回收上的時間比例減少年輕代和年老代花費的時間,一般會提高應用的效率

吞吐量優先的應用

一般吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代。原因是,這樣可以盡可能回收掉大部分短期對象,減少中期對象,而年老代盡存放長期存活的對象

較小堆引起的碎片問題

因為年老代的并發收集器使用标記-清除算法,是以不會對堆進行壓縮。當收集器回收時,他會把相鄰的空間進行合并,這樣可以配置設定給較大的對象。但是當堆空間較小時,運作一段時間以後,就會出現“碎片”,如果并發收集器找不到足夠的空間,那麼并發收集器将會停止,然後使用傳統的标記、清除方式進行回收。如果出現“碎片”,可能需要進行如下配置:

-XX:+UseCMSCompactAtFullCollection:使用并發收集器時,開啟對年老代的壓縮

-XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這裡設定多少次FullGc後,對年老代進行壓縮

結語:之前也沒怎麼寫過部落格,最近開始慢慢把自己學習的知識總結一遍加深了解,如有錯誤遺漏之處還請一起讨論指出。 文章摘取了各大部落客的片段及自己的總結,已表明參考文章,還請勿怪!

參考文章:

https://blog.csdn.net/xiaojie_570/article/details/80395066

https://www.cnblogs.com/aspirant/p/8662690.html

https://www.liangzl.com/get-article-detail-17354.html