前言
之前我們講解了jvm的組成結構與垃圾回收算法等知識點,今天我們來講講jvm最重要的堆內存是如何使用垃圾回收器進行垃圾回收,並且如何使用命令去配置使用這些垃圾回收器。
堆內存詳解

上面這個圖大家應該已經很明白了吧。大家就可以理解成一個房子被分成了幾個房間,每個房間的作用不同而已,有的是嬰兒住的,有的是父母住的,有的是爺爺奶奶住的
-
堆內存被划分為
兩塊,一塊的年輕代,另一塊是老年代。 -
年輕代又分為
Eden和survivor。他倆空間大小比例默認為8:2, -
幸存區又分為
s0和s1。這兩個空間大小是一模一樣的,就是一對雙胞胎,他倆是1:1的比例
堆內存垃圾回收過程
第一步
新生成的對象首先放到Eden區,當Eden區滿了會觸發Minor GC。
第二步
第一步GC活下來的對象,會被移動到survivor區中的S0區,S0區滿了之后會觸發Minor GC,S0區存活下來的對象會被移動到S1區,S0區空閑。
S1滿了之后在GC,存活下來的再次移動到S0區,S1區空閑,這樣反反復復GC,每GC一次,對象的年齡就漲一歲,達到某個值后(15),就會進入老年代。
第三步
在發生一次Minor GC后(前提條件),老年代可能會出現Major GC,這個視垃圾回收器而定。
Full GC觸發條件
-
手動調用System.gc,會不斷的執行Full GC
-
老年代空間不足/滿了
-
方法區空間不足/滿了
注意
們需要記住一個單詞:stop-the-world。它會在任何一種GC算法中發生。stop-the-world 意味着JVM因為需要執行GC而停止應用程序的執行。
當stop-the-world 發生時,除GC所需的線程外,所有的線程都進入等待狀態,直到GC任務完成。GC優化很多時候就是減少stop-the-world 的發生。
回收哪些區域的對象
需要注意的是,JVM GC只回收堆內存和方法區內的對象。而棧內存的數據,在超出作用域后會被JVM自動釋放掉,所以其不在JVM GC的管理范圍內。
堆內存常見參數配置
| 參數 | 描述 |
|---|---|
| -Xms | 堆內存初始大小,單位m、g |
| -Xmx | 堆內存最大允許大小,一般不要大於物理內存的80% |
| -XX:PermSize | 非堆內存初始大小,一般應用設置初始化200m,最大1024m就夠了 |
| -XX:MaxPermSize | 非堆內存最大允許大小 |
| -XX:NewSize(-Xns) | 年輕代內存初始大小 |
| -XX:MaxNewSize(-Xmn) | 年輕代內存最大允許大小 |
| -XX:SurvivorRatio=8 | 年輕代中Eden區與Survivor區的容量比例值,默認為8,即8:1 |
| -Xss | 堆棧內存大小 |
| -XX:NewRatio=老年代/新生代 | 設置老年代和新生代的大小比例 |
| -XX:+PrintGC | jvm啟動后,只要遇到GC就會打印日志 |
| -XX:+PrintGCDetails | 查看GC詳細信息,包括各個區的情況 |
| -XX:MaxDirectMemorySize | 在NIO中可以直接訪問直接內存,這個就是設置它的大小,不設置默認就是最大堆空間的值-Xmx |
| -XX:+DisableExplicitGC | 關閉System.gc() |
| -XX:MaxTenuringThreshold | 垃圾可以進入老年代的年齡 |
| -Xnoclassgc | 禁用垃圾回收 |
| -XX:TLABWasteTargetPercent | TLAB占eden區的百分比,默認是1% |
| -XX:+CollectGen0First | FullGC時是否先YGC,默認false |
TLAB 內存
TLAB全稱是Thread Local Allocation Buffer即線程本地分配緩存,從名字上看是一個線程專用的內存分配區域,是為了加速對象分配而生的。
每一個線程都會產生一個TLAB,該線程獨享的工作區域,java虛擬機使用這種TLAB區來避免多線程沖突問題,提高了對象分配的效率。
TLAB空間一般不會太大,當大對象無法在TLAB分配時,則會直接分配到堆上。
| 參數 | 描述 |
|---|---|
| -Xx:+UseTLAB | 使用TLAB |
| -XX:+TLABSize | 設置TLAB大小 |
| -XX:TLABRefillWasteFraction | 設置維護進入TLAB空間的單個對象大小,他是一個比例值,默認為64,即如果對象大於整個空間的1/64,則在堆創建 |
| -XX:+PrintTLAB | 查看TLAB信息 |
| -Xx:ResizeTLAB | 自調整TLABRefillWasteFraction閥值。 |

垃圾回收器總覽

新生代可配置的回收器:Serial、ParNew、Parallel Scavenge
老年代配置的回收器:CMS、Serial Old、Parallel Old
新生代和老年代區域的回收器之間進行連線,說明他們之間可以搭配使用。
新生代垃圾回收器
Serial 垃圾回收器
Serial收集器是最基本的、發展歷史最悠久的收集器。俗稱為:串行回收器,采用復制算法進行垃圾回收
特點
串行回收器是指使用單線程進行垃圾回收的回收器。每次回收時,串行回收器只有一個工作線程。
對於並行能力較弱的單CPU計算機來說,串行回收器的專注性和獨占性往往有更好的性能表現。
它存在Stop The World問題,及垃圾回收時,要停止程序的運行。
使用-XX:+UseSerialGC參數可以設置新生代使用這個串行回收器
ParNew 垃圾回收器
ParNew其實就是Serial的多線程版本,除了使用多線程之外,其余參數和Serial一模一樣。俗稱:並行垃圾回收器,采用復制算法進行垃圾回收
特點
ParNew默認開啟的線程數與CPU數量相同,在CPU核數很多的機器上,可以通過參數-XX:ParallelGCThreads來設置線程數。
它是目前新生代首選的垃圾回收器,因為除了ParNew之外,它是唯一一個能與老年代CMS配合工作的。
它同樣存在Stop The World問題
使用-XX:+UseParNewGC參數可以設置新生代使用這個並行回收器
ParallelGC 回收器
ParallelGC使用復制算法回收垃圾,也是多線程的。
特點
就是非常關注系統的吞吐量,吞吐量=代碼運行時間/(代碼運行時間+垃圾收集時間)
-XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間,可用把虛擬機在GC停頓的時間控制在MaxGCPauseMillis范圍內,如果希望減少GC停頓時間可以將MaxGCPauseMillis設置的很小,但是會導致GC頻繁,從而增加了GC的總時間,降低了吞吐量。所以需要根據實際情況設置該值。
-Xx:GCTimeRatio:設置吞吐量大小,它是一個0到100之間的整數,默認情況下他的取值是99,那么系統將花費不超過1/(1+n)的時間用於垃圾回收,也就是1/(1+99)=1%的時間。
另外還可以指定-XX:+UseAdaptiveSizePolicy打開自適應模式,在這種模式下,新生代的大小、eden、from/to的比例,以及晉升老年代的對象年齡參數會被自動調整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。
使用-XX:+UseParallelGC參數可以設置新生代使用這個並行回收器
老年代垃圾回收器
SerialOld 垃圾回收器
SerialOld是Serial回收器的老年代回收器版本,它同樣是一個單線程回收器。
用途
-
一個是在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用,
-
另一個就是作為CMS收集器的后備預案,如果CMS出現Concurrent Mode Failure,則SerialOld將作為后備收集器。
使用算法:標記 - 整理算法
ParallelOldGC 回收器
老年代ParallelOldGC回收器也是一種多線程的回收器,和新生代的ParallelGC回收器一樣,也是一種關注吞吐量的回收器,他使用了標記壓縮算法進行實現。
-XX:+UseParallelOldGc進行設置老年代使用該回收器
-XX:+ParallelGCThreads也可以設置垃圾收集時的線程數量。
CMS 回收器
CMS全稱為:Concurrent Mark Sweep意為並發標記清除,他使用的是標記清除法。主要關注系統停頓時間。
使用-XX:+UseConcMarkSweepGC進行設置老年代使用該回收器。
使用-XX:ConcGCThreads設置並發線程數量。
特點
CMS並不是獨占的回收器,也就說CMS回收的過程中,應用程序仍然在不停的工作,又會有新的垃圾不斷的產生,所以在使用CMS的過程中應該確保應用程序的內存足夠可用。
CMS不會等到應用程序飽和的時候才去回收垃圾,而是在某一閥值的時候開始回收,回收閥值可用指定的參數進行配置:-XX:CMSInitiatingoccupancyFraction來指定,默認為68,也就是說當老年代的空間使用率達到68%的時候,會執行CMS回收。
如果內存使用率增長的很快,在CMS執行的過程中,已經出現了內存不足的情況,此時CMS回收就會失敗,虛擬機將啟動老年代串行回收器;SerialOldGC進行垃圾回收,這會導致應用程序中斷,直到垃圾回收完成后才會正常工作。
這個過程GC的停頓時間可能較長,所以-XX:CMSInitiatingoccupancyFraction的設置要根據實際的情況。
之前我們在學習算法的時候說過,標記清除法有個缺點就是存在內存碎片的問題,那么CMS有個參數設置-XX:+UseCMSCompactAtFullCollecion可以使CMS回收完成之后進行一次碎片整理。
-XX:CMSFullGCsBeforeCompaction參數可以設置進行多少次CMS回收之后,對內存進行一次壓縮。
G1 回收器
篇幅太長,我們下篇文章講解!!!

IT 老哥
一個在大廠做高級Java開發的程序猿
