4、JVM垃圾回收机制、新生代的GC、GC(Minor GC、FullGC)、GC日志、JVM参数选项、元空间(笔记)
4.JVM垃圾回收機制
4.1.新生代的GC
4.1.1.串行GC(SerialGC)
4.1.2.并行回收GC(Parallel Scavenge)
4.1.3.并行GC(ParNew)
4.2.GC(Minor GC、FullGC)
4.2.1.Minor GC
4.2.2.FullGC
4.3.GC日志
4.JVM垃圾回收機制
JVM分別對新生代和老年代采用不同的垃圾回收機制。
4.1.新生代的GC
新生代通常存活時間較短,因此基于復(fù)制算法來進行回收,所謂復(fù)制算法就是掃描出存活的對象,并復(fù)制到一塊新的完全未使用的空間中,對應(yīng)于新生代,就是在Eden和其中一個Survivor,復(fù)制到另一個之間Survivor空間中,然后清理掉原來就是在Eden和其中一個Survivor中的對象。新生代采用空閑指針的方式來控制GC觸發(fā),指針保持最后一個分配的對象在新生代區(qū)間的位置,當有新的對象要分配內(nèi)存時,用于檢查空間是否足夠,不夠就觸發(fā)GC。當連續(xù)分配對象時,對象會逐漸從eden到 survivor,最后到老年代。
用java visualVM來查看,能明顯觀察到新生代滿了后,會把對象轉(zhuǎn)移到舊生代,然后清空繼續(xù)裝在,當舊生代也滿了后,就會報OutOfMemory的異常。
在執(zhí)行機制上JVM提供了串行GC(SerialGC)、并行回收GC(ParallelScavenge)和并行GC(ParNew)。
4.1.1.串行GC(SerialGC)
在整個掃描和復(fù)制過程采用單線程的方式來進行,適用于單CPU、新生代空間較小及對暫停時間要求不是非常高的應(yīng)用上,是client級別默認的GC方式,可以通過-XX:+UseSerialGC來強制指定。
4.1.2.并行回收GC(Parallel Scavenge)
在整個掃描和復(fù)制過程采用多線程的方式來進行,適用于多CPU、對暫停時間要求較短的應(yīng)用上,是server級別默認采用的GC方式,可用-XX:+UseParallelGC來強制指定,用-XX:ParallelGCThreads=4來指定線程數(shù)。
4.1.3.并行GC(ParNew)
與舊生代的并發(fā)GC配合使用。
舊生代的GC:
舊生代與新生代不同,對象存活的時間比較長,比較穩(wěn)定,因此采用標記(Mark)算法來進行回收,所謂標記就是掃描出存活的對象,然后再進行回收未被標記的對象,回收后對用空出的空間要么進行合并,要么標記出來便于下次進行分配,總之就是要減少內(nèi)存碎片帶來的效率損耗。在執(zhí)行機制上JVM提供了串行 GC(SerialMSC)、并行GC(parallelMSC)和并發(fā)GC(CMS),具體算法細節(jié)還有待進一步深入研究。
4.2.GC(Minor GC、FullGC)
GC分為兩種:Minor GC、FullGC(或稱為Major GC)。
4.2.1.Minor GC
從年輕代空間(包括 Eden 和 Survivor 區(qū)域)回收內(nèi)存被稱為Minor GC。
當發(fā)生Minor GC事件的時候,有一些有趣的地方需要注意到:
?當 JVM 無法為一個新的對象分配空間時會觸發(fā) Minor GC,比如當 Eden 區(qū)滿了。所以分配率越高,越頻繁執(zhí)行 Minor GC。
?內(nèi)存池被填滿的時候,其中的內(nèi)容全部會被復(fù)制,指針會從0開始跟蹤空閑內(nèi)存。Eden 和 Survivor 區(qū)進行了標記和復(fù)制操作,取代了經(jīng)典的標記、掃描、壓縮、清理操作。所以 Eden 和 Survivor 區(qū)不存在內(nèi)存碎片。寫指針總是停留在所使用內(nèi)存池的頂部。
?執(zhí)行 Minor GC 操作時,不會影響到永久代。從永久代到年輕代的引用被當成 GC roots,從年輕代到永久代的引用在標記階段被直接忽略掉。
?質(zhì)疑常規(guī)的認知,所有的 Minor GC都會觸發(fā)”stop-the-world”,停止應(yīng)用程序的線程。對于大部分應(yīng)用程序,停頓導(dǎo)致的延遲都是可以忽略不計的。其中的真相就是,大部分 Eden區(qū)中的對象都能被認為是垃圾,永遠也不會被復(fù)制到Survivor區(qū)或者老年代空間。如果正好相反,Eden 區(qū)大部分新生對象不符合 GC 條件,Minor GC 執(zhí)行時暫停的時間將會長很多。
每次Minor GC會清理年輕代的內(nèi)存。
Minor GC是發(fā)生在新生代中的垃圾收集動作,所采用的復(fù)制算法。
當一個對象被判定為”死亡”的時候,GC就有責任來回收掉這部分對象的內(nèi)存空間。新生代是GC收集垃圾的頻繁區(qū)域。
當對象在Eden (包括一個 Survivor 區(qū)域,這里假設(shè)是 from 區(qū)域)出生后,在經(jīng)過一次Minor GC后,如果對象還存活,并且能夠被另外一塊Survivor區(qū)域所容納(上面已經(jīng)假設(shè)為 from 區(qū)域,這里應(yīng)為to 區(qū)域,即to區(qū)域有足夠的內(nèi)存空間來存儲Eden和from區(qū)域中存活的對象),則使用復(fù)制算法將這些仍然還存活的對象復(fù)制到另外一塊Survivor區(qū)域(即to區(qū)域)中,然后清理所使用過的Eden以及Survivor區(qū)域(即from區(qū)域),并且將這些對象的年齡設(shè)置為1,以后對象在Survivor 區(qū)每熬過一次 Minor GC,就將對象的年齡+ 1,當對象的年齡達到某個值時 ( 默認是 15 歲,可以通過參數(shù) -XX:MaxTenuringThreshold來設(shè)定),這些對象就會成為老年代。
但這也不是一定的,對于一些較大的對象(即需要分配一塊較大的連續(xù)內(nèi)存空間)則是直接進入到老年代。
4.2.2.FullGC
Full GC是發(fā)生在老年代的垃圾收集動作,所采用的是標記-清除算法。
現(xiàn)實的生活中,老年代的人通常會比新生代的人”早死”。堆內(nèi)存中的老年代(Old)不同于這個老年代里面的對象幾乎個個都是在Survivor區(qū)域中熬過來的,它們是不會那么容易就”死掉”了的。
因此,Full GC發(fā)生的次數(shù)不會有Minor GC那么頻繁,并且做一次Full GC要比進行一次Minor GC的時間更長。
另外,標記-清除算法收集垃圾的時候會產(chǎn)生許多的內(nèi)存碎片(即不連續(xù)的內(nèi)存空間),此后需要為較大的對象分配內(nèi)存空間時,若無法找到足夠的連續(xù)的內(nèi)存空間,就會提前觸發(fā)一次GC的收集動作。
4.3.GC日志
案例
package com.toto.jvm;public class Demo {public static void main(String[] args) {Object obj = new Object();System.gc();System.out.println();obj = new Object();obj = new Object();System.gc();System.out.println();} }設(shè)置VM arguments:-verbose:gc -XX:+PrintGCDetails
Run之后,輸出結(jié)果:
System.gc()可以觸發(fā)Full GC
[GC (System.gc()) [PSYoungGen: 5201K->752K(151552K)] 5201K->760K(498688K), 0.0012719 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 752K->0K(151552K)] [ParOldGen: 8K->536K(347136K)] 760K->536K(498688K), [Metaspace: 2587K->2587K(1056768K)], 0.0088355 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [GC (System.gc()) [PSYoungGen: 2600K->96K(151552K)] 3137K->632K(498688K), 0.0005806 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 96K->0K(151552K)] [ParOldGen: 536K->535K(347136K)] 632K->535K(498688K), [Metaspace: 2588K->2588K(1056768K)], 0.0064823 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] HeapPSYoungGen total 151552K, used 2601K [0x0000000716d00000, 0x0000000721600000, 0x00000007c0000000)eden space 130048K, 2% used [0x0000000716d00000,0x0000000716f8a578,0x000000071ec00000)from space 21504K, 0% used [0x0000000720100000,0x0000000720100000,0x0000000721600000)to space 21504K, 0% used [0x000000071ec00000,0x000000071ec00000,0x0000000720100000)ParOldGen total 347136K, used 535K [0x00000005c4600000, 0x00000005d9900000, 0x0000000716d00000)object space 347136K, 0% used [0x00000005c4600000,0x00000005c4685f60,0x00000005d9900000)Metaspace used 2594K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 288K, capacity 386K, committed 512K, reserved 1048576K設(shè)置JVM參數(shù)為-verbose:gc -XX:+PrintGCDetails,使得控制臺能夠顯示GC相關(guān)的日志信息,執(zhí)行上面代碼,下面是其中一次執(zhí)行的結(jié)果。
jstate -gc -t 4235 1s5.JVM參數(shù)選項
下面只列舉其中的幾個常用和容易掌握的配置選項
| -Xms | 初始堆大小。如:-Xms256m |
| -Xmx | 最大堆大小。如:-Xmx512m |
| -Xmn | 新生代大小。通常為Xmx的1/3 或1/4。新生代 = Eden + 2個Survivor空間。實際可用空間為 = Eden + 1 個Survivor,即90% |
| -Xss | JDK1.5+每個線程堆棧大小為1M,一般來說如果棧不是很深的話,1M是絕對夠用了的。 |
| -XX:NewRatio | 新生代與老年代的比例,如–XX:NewRatio=2,則新生代占整個堆空間的1/3,老年代占2/3 |
| -XX:SurvivorRatio | 新生代中Eden與Survivor 的比值。默認值為8。即 Eden占新生代空間的8/10,另外兩個 Survivor 各占 1/10 |
| -XX:PermSize | 永久代(方法區(qū))的初始大小 |
| -XX:MaxPermSize | 永久代(方法區(qū))的最大值 |
| -XX:+PrintGCDetails | 打印GC信息 |
| -XX:+HeapDumpOnOutOfMemoryError | 讓虛擬機在發(fā)生內(nèi)存溢出時Dump出當前的內(nèi)存堆轉(zhuǎn)儲快照,以便分析用。 |
-XX:Newratio: 設(shè)置Old和Yong的比例,比如值為2,則Old Generation是 Yong Generation的2倍,即Yong Generation占據(jù)內(nèi)存的1/3
-XX:Newsize : 設(shè)置Yong Generation的初始值大小
-XX:Maxnewsize:設(shè)置Yong Generation的最大值大小
-XX:Surviorratio : 設(shè)置Eden和一個Suivior的比例,比如值為5,即Eden是To(S2)的比例是5,(From和To是一樣大的),此時Eden占據(jù)Yong Generation的5/7
一般情況下,不允許-XX:Newratio值小于1,即Old要比Yong大。
6.元空間
元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內(nèi)存。因此,默認情況下,元空間的大小受本地內(nèi)存限制。
總結(jié)
以上是生活随笔為你收集整理的4、JVM垃圾回收机制、新生代的GC、GC(Minor GC、FullGC)、GC日志、JVM参数选项、元空间(笔记)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 创投板块有哪些上市公司 一笔投资可能就改
- 下一篇: 安逸花云闪付怎么提现