GC算法是内存回收的方法论,垃圾收集器就是算法落地实现。
GC算法:引用计数,复制,标记清除,标记整理
目前没有完美的收集器,没有万能的收集器,只是针对具体应用最适合的收集器,进行分代收集

四种主要垃圾收集器

1648650212(1).png

串行垃圾回收器(Serial)

为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程,所以不适合服务器环境

并行垃圾回收器(parallel)

多个垃圾线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理首台处理等若交互场景。

并发垃圾收集器 (CMS)

  • 并发标记清除

用户线程和垃圾收集线程同时执行,不一定是并行,可能是交替进行,不需要停顿用户线程
互联网公司多用它,使用对响应时间有要求的场景

小总结

image.png

G1垃圾回收器

将堆内存分割成多个区域然后分块回收垃圾

怎么查看服务器默认的垃圾收集器是什么?

D:\code\my-interview-demo>java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=263489344 -XX:MaxHeapSize=4215829504 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+U seParallelGC java version “1.8.0_241” Java(TM) SE Runtime Environment (build 1.8.0_241-b07) Java HotSpot(TM) 64-Bit Server VM (build 25.241-b07, mixed mode)

默认: 并行回收

JVM默认的垃圾收集器有哪些?

image.png

  • UseSerialOldGC (已废弃)
  • 串行回收: -XX:+UseSerialGC
  • 并行回收: -XX:+UseParallelGC
  • 并发回收(CMS-并发标记清除GC): -XX:+ConcMarkSweepGC
  • 年轻代并行GC: UseParNewGC
  • 老年代的并发GC:UseParallelOldGC
  • UseG1GC

    垃圾回收器(重点)

    image.png
    新生代:串行垃圾回收器,并行垃圾回收器,新生区并行垃圾回收器
    G1:不再严格区分Young区和Old区
    image.png

    部分参数预先说明

  • DefNew: Default New Generation

  • Tenured: Old
  • ParNew: Parallel New Generation
  • PSYoungGen: Parallel Scavenge
  • ParOldGen: Parallel Old Generation

    Server/Client模式分别是什么意思?

  1. 适用范围: 只需要掌握Server模式,Client模式基本不会用
  2. 操作系统
  • 32位Windows系统,不论硬件都是默认Client的JVM模式
  • 32位其他操作系统,2G内存同时有2个CPU以上用Server模式,低于该配置Client模式
  • 64位only Server模式

    新生代

    串行GC(Serial)/(Serial Copying)

    单线程的收集器,进行垃圾收集的时候,必须暂停其他所有的工作线程直到它收集结束
    image.png

  • 稳定效率高,只有一个线程回收

  • 可能产生较长的停顿(Stop the World)
  • 限定单个CPU环境
  • Client下的新生代

对应的JVM参数: -XX:+UseSerialGC
开启后会使用:Serial(Young区)+ Serial Old(old区用)的收集器组合
表示:新生代、老年代都会使用串行回收收集器,新生地啊使用复制算法,老年代使用标记-整理算法

-Xmx10m -Xms10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC

image.png

并行GC(ParNew)

image.png
image.png
image.png

并行回收GC算法(Parallel)

image.png
并行收集器组合: Parallel Scavenge + Parallel Old
image.png
image.png
开启之后,新生代使用复制算法,老年代使用标记-整理算法
image.png

老年代

串行GC(Serial Old) / (Serial MSC)

image.png

Parallel Old

image.png

并发标记清除GC(CMS)

image.png
image.png
image.png

4个过程
  1. 初始标记:只是标记一下GC Roots能直接关联的对象,速度很快,仍需暂停所有的工作线程。
  2. 并发标记: 进行GC Roots跟踪的过程,和用户线程一起,不需要暂停工作线程,主要标记过程,标记全部对象
  3. 重新标记: 修改并发标记期间,因用户程序继续运行而导致标记产生标记的那一部分对象的标记,仍需暂停所有的工作线程
  4. 并发清除:清除GC Roots不可达的对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果,直接清理随想。由于耗时交叉给的并发标记和并发清除过程中,垃圾收集线程可以和用户线程一起并发工作,所以总体上来看CMS收集器的内存回收和用户线程是一起并发的执行。

image.png

优缺点
  • 优点:并发收集低停顿
  • 缺点: 并发执行,对CPU压力比较大;采用标记清除算法会导致大量的碎片。

    由于并发进行,CMS在收集与应用线程会同时增加对堆内存的占用,也就是说,CMS必须在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败时,将触发担保机制,串行老年代收集器将会以STW的方式进行一次GC,从而造成较大的停顿时间。 标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过担保机制堆堆内存进行压缩。CMS也提供了参数 -XX:CMSFullGCBeForeCompaction(默认0,即每次都进行内存整理)来指定多少次CMS收集之后,进行一次压缩的Full Gc

如何选择垃圾回收器?

image.png
image.png

G1垃圾收集器

-Xmx10m -Xms10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC

image.png
没有像之前有新生代老年代。G1横跨New Old两区。

以前收集器的特点

  • 年轻代和老年代是各自独立且连续的内存块
  • 年轻代收集采用eden + s0 + s1进行复制算法
  • 老年代收集必须扫描整个老年代区域
  • 都是以尽可能少而快速地执行GC为设计原则

    什么是G1

    G1(garbage-First收集器),是一款面向服务端应用的收集器。应用在多处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能满足垃圾收集暂停时间的要求。另外,还具有以下特性:

  • 像CMS收集器一样,能与应用程序线程并发执行

  • 整理空闲空间更快
  • 需要更多的时间来预测GC停顿时间
  • 不希望牺牲大量的吞吐性能
  • 不需要更大的Java Heap

G1收集器的设计目标是取代CMS,同CMS相比,在以下方面表现更出色:

  • G1是一个有整理内存过程的垃圾收集器,不会产生很多的内存碎片
  • G1的stop the world更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间

JDK9 默认垃圾收集器G1,替代CMS。

主要改变是Eden,Survivor 和Thurned 等内存区域不再是连续的了,而是变成了一个个大小一样的region, 每个region从1M到32M不等,化整为零。一个region有可能属于Eden,Survivor或者Tenured内存区域。

特点

  1. 充分利用多CPU、多核环境硬件优势,尽量缩短STW
  2. 整体采用标记-整理算法,局部是通过复制算法,不会产生内存碎片
  3. 宏观上看G1之中不再区分年轻代和老年代,把内存划分多个独立的子区域(Region),可以近似理解为一个围棋的棋盘。
  4. G1收集器里面将整个的内存区域都混合在一起了,但其本身依然在小范围要进行年轻代和老年代的区分,保留了新生代和老年代,但他们不再是物理隔离的,而是一部分Region的集合且不需要Region是连续的,也就是说依然会采用不同的GC方式来处理不同的区域
  5. G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代和老年代的区别。也不需要完全独立的survivor堆做复制准备,G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换。

    底层原理

    Region区域化垃圾收集器

    最大的好处就是化整为零,避免全内存扫描,只需要按照区域扫描。
    区域化内存划片Region,整体编为了一些列不连续的内存区域,避免了全内存区的GC操作。
    核心思想是将整个内存区域划分为大小相同的子区域Region,在JVM启动时会自动设置这些子区域的大小。在堆的使用上,G1并不要求对象的存储一定是物理上连续的只要逻辑上连续即可。每个区域也不会固定的为某个代服务,可以按需在年轻代和老年代之间切换,启动时可以通过参数-XX:G1HeapRegionSize=n可指定分区大小(1M~32M且必须是2的幂),默认将整体划分为2048个分区。
    大小范围在1MB~32MB, 最多能设置2048个分区,也即最大支持的内存为32MB * 2048 = 64G内存
    image.png
    Humongous: 超大对象区
    image.png
    image.png

    回收步骤

    G1收集器下的Young GC
    针对Eden区进行收集,Eden区耗尽后会被触发,主要是小区域收集+形成连续的内存块,避免内存碎片
  • Eden区的数据移动到Survivor区域,假如出现Survivor区空间不够,Eden区数据会局部晋升到Old区
  • Survivor区数据移动到新的Survivor区,部分数据晋升到Old区
  • 最后Eden区收拾干净了,GC结束了,用户的应用程序继续进行。

    4个过程
  • 初始标记:只标记GC roots能直接关联到的对象

  • 并发标记:进行GC Roots Tracing的过程
  • 最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
  • 筛选回收:根据时间来进行价值最大化的回收

image.png

case

D:\programtools\jdk\bin\java.exe -Dvisualvm.id=227759288521600 -Xmx10m -Xms10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC -javaagent:D:\tools\IntelliJU\lib\idea_rt.jar=58875:D:\tools\IntelliJU\bin -Dfile.encoding=UTF-8 -classpath D:\programtools\jdk\jre\lib\charsets.jar;D:\programtools\jdk\jre\lib\deploy.jar;D:\programtools\jdk\jre\lib\ext\access-bridge-64.jar;D:\programtools\jdk\jre\lib\ext\cldrdata.jar;D:\programtools\jdk\jre\lib\ext\dnsns.jar;D:\programtools\jdk\jre\lib\ext\jaccess.jar;D:\programtools\jdk\jre\lib\ext\jfxrt.jar;D:\programtools\jdk\jre\lib\ext\localedata.jar;D:\programtools\jdk\jre\lib\ext\nashorn.jar;D:\programtools\jdk\jre\lib\ext\sunec.jar;D:\programtools\jdk\jre\lib\ext\sunjce_provider.jar;D:\programtools\jdk\jre\lib\ext\sunmscapi.jar;D:\programtools\jdk\jre\lib\ext\sunpkcs11.jar;D:\programtools\jdk\jre\lib\ext\zipfs.jar;D:\programtools\jdk\jre\lib\javaws.jar;D:\programtools\jdk\jre\lib\jce.jar;D:\programtools\jdk\jre\lib\jfr.jar;D:\programtools\jdk\jre\lib\jfxswt.jar;D:\programtools\jdk\jre\lib\jsse.jar;D:\programtools\jdk\jre\lib\management-agent.jar;D:\programtools\jdk\jre\lib\plugin.jar;D:\programtools\jdk\jre\lib\resources.jar;D:\programtools\jdk\jre\lib\rt.jar;D:\code\my-interview-demo\target\classes;D:\maven-repository\com\alibaba\fastjson\1.2.13\fastjson-1.2.13.jar;D:\maven-repository\com\google\guava\guava\27.0.1-jre\guava-27.0.1-jre.jar;D:\maven-repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;D:\maven-repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;D:\maven-repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;D:\maven-repository\org\checkerframework\checker-qual\2.5.2\checker-qual-2.5.2.jar;D:\maven-repository\com\google\errorprone\error_prone_annotations\2.2.0\error_prone_annotations-2.2.0.jar;D:\maven-repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;D:\maven-repository\org\codehaus\mojo\animal-sniffer-annotations\1.17\animal-sniffer-annotations-1.17.jar;D:\maven-repository\org\projectlombok\lombok\1.18.0\lombok-1.18.0.jar;D:\maven-repository\cn\hutool\hutool-all\4.3.1\hutool-all-4.3.1.jar;D:\maven-repository\org\apache\commons\commons-lang3\3.9\commons-lang3-3.9.jar;D:\maven-repository\commons-codec\commons-codec\1.10\commons-codec-1.10.jar;D:\maven-repository\cglib\cglib\3.3.0\cglib-3.3.0.jar;D:\maven-repository\org\ow2\asm\asm\7.1\asm-7.1.jar com.interview.demo.HelloGC -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC -XX:-UseLargePagesIndividualAllocation hello gc [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0013376 secs] [Parallel Time: 1.0 ms, GC Workers: 8] [GC Worker Start (ms): Min: 180.0, Avg: 180.0, Max: 180.3, Diff: 0.3] [Ext Root Scanning (ms): Min: 0.2, Avg: 0.4, Max: 0.6, Diff: 0.4, Sum: 3.0] [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0] [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Object Copy (ms): Min: 0.2, Avg: 0.3, Max: 0.4, Diff: 0.2, Sum: 2.8] [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Termination Attempts: Min: 1, Avg: 8.6, Max: 13, Diff: 12, Sum: 69] [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.3] [GC Worker Total (ms): Min: 0.5, Avg: 0.8, Max: 0.8, Diff: 0.3, Sum: 6.1] [GC Worker End (ms): Min: 180.8, Avg: 180.8, Max: 180.8, Diff: 0.0] [Code Root Fixup: 0.0 ms] [Code Root Purge: 0.0 ms] [Clear CT: 0.1 ms] [Other: 0.3 ms] [Choose CSet: 0.0 ms] [Ref Proc: 0.2 ms] [Ref Enq: 0.0 ms] [Redirty Cards: 0.1 ms] [Humongous Register: 0.0 ms] [Humongous Reclaim: 0.0 ms] [Free CSet: 0.0 ms] [Eden: 2048.0K(6144.0K)->0.0B(4096.0K) Survivors: 0.0B->1024.0K Heap: 1932.1K(10.0M)->856.0K(10.0M)] [Times: user=0.03 sys=0.00, real=0.01 secs] [GC concurrent-root-region-scan-start] [GC pause (G1 Humongous Allocation) (young)[GC concurrent-root-region-scan-end, 0.0005833 secs] [GC concurrent-mark-start] , 0.0019943 secs] [Root Region Scan Waiting: 0.4 ms] [Parallel Time: 1.0 ms, GC Workers: 8] [GC Worker Start (ms): Min: 182.0, Avg: 182.2, Max: 182.5, Diff: 0.5] [Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8] [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0] [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Object Copy (ms): Min: 0.3, Avg: 0.5, Max: 0.7, Diff: 0.4, Sum: 4.4] [Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.4] [Termination Attempts: Min: 1, Avg: 7.8, Max: 14, Diff: 13, Sum: 62] [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [GC Worker Total (ms): Min: 0.4, Avg: 0.7, Max: 0.9, Diff: 0.5, Sum: 5.7] [GC Worker End (ms): Min: 182.9, Avg: 182.9, Max: 182.9, Diff: 0.0] [Code Root Fixup: 0.0 ms] [Code Root Purge: 0.0 ms] [Clear CT: 0.1 ms] [Other: 0.4 ms] [Choose CSet: 0.0 ms] [Ref Proc: 0.2 ms] [Ref Enq: 0.0 ms] [Redirty Cards: 0.1 ms] [Humongous Register: 0.0 ms] [Humongous Reclaim: 0.0 ms] [Free CSet: 0.0 ms] [Eden: 0.0B(4096.0K)->0.0B(4096.0K) Survivors: 1024.0K->0.0B Heap: 856.0K(10.0M)->687.5K(10.0M)] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC concurrent-mark-end, 0.0018435 secs] [Full GC (Allocation Failure) 687K->668K(10M), 0.0037291 secs] [Eden: 0.0B(4096.0K)->0.0B(4096.0K) Survivors: 0.0B->0.0B Heap: 687.5K(10.0M)->668.6K(10.0M)], [Metaspace: 3124K->3124K(1056768K)] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) 668K->650K(10M), 0.0028907 secs] [Eden: 0.0B(4096.0K)->0.0B(4096.0K) Survivors: 0.0B->0.0B Heap: 668.6K(10.0M)->650.6K(10.0M)], [Metaspace: 3124K->3124K(1056768K)] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC remark, 0.0000094 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC concurrent-mark-abort] Exception in thread “main” java.lang.OutOfMemoryError: Java heap space at com.interview.demo.HelloGC.main(HelloGC.java:10) Heap garbage-first heap total 10240K, used 650K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000) region size 1024K, 1 young (1024K), 0 survivors (0K) Metaspace used 3249K, capacity 4496K, committed 4864K, reserved 1056768K class space used 351K, capacity 388K, committed 512K, reserved 1048576K

  1. Process finished with exit code 1

常用配置参数(了解)

  • -XX:UseG1GC
  • -XX:G1HeapRegionSize:设置G1区域的大小
  • -XX:MaxGCPauseMills=n: 设置GC停顿时间,这个是一个软目标,JVM尽可能(但不保证)停顿小于这个时间
  • -XX:InitiatingHeapOccupancyPercent=n:堆占用多少时触发GC,默认是45
  • -XX:ConcGCThreads=n : 并发GC使用的线程数
  • -XX:G1ReservePercent=n : 设置作为空闲时间的预留内存百分比,以降低目标空间溢出的风险,默认是10%

    和CMS相比优势

  • G1没有内存碎片

  • 可以精确控制停顿。