JVM知识点思维导图
Figure 1. JVM知识点思维导图

1. JVM概述

JVM是一个支持在不同操作系统上执行Java字节码的运行时容器, 拥有内存管理/垃圾回收等功能.
Java字节码无法直接执行, JVM需要将其翻译成机器码.
在HotSpot里, 该翻译过程有两种形式:

  • 解释执行: 逐条将字节码翻译成机器码.

  • 即时编译执行: 将一个方法中所有的字节码翻译成机器码后再执行.

HotSpot内置多种编译器:

  • C1 启动块,编译时间短

  • C2 编译时间比较长,但执行效率高

  • graal

2. Class文件组成

  • magic number

  • minor&major versions

  • constant pool

  • access flags

  • this class

  • super class

  • interfaces

  • fields

  • methods

  • attributes

3. 类加载器

3.1. 获取类加载器的方法

  • 获取某一个类所属的类加载器: Class::getClassLoader()

  • 获取APP类加载器: ClassLoader.getSystemClassLoader()

  • 获取当前线程上下文使用的类加载器: Thread.currentThread().getContextClassLoader()

3.2. BootstrapClassLoader

启动类加载器.
本身使用C/C++实现, 没有父加载器.
通过 sun.misc.Launcher.getBootstrapClassPath().getURLs() 获取加载类的路径.

3.3. ExtClassLoader

加载jre/lib/ext子目录下的类库, 父加载器为BootstrapClassLoader.
通过 System.getProperty("java.ext.dirs") 获取加载类的路径.

3.4. AppClassLoader

加载classpath/java.class.path路径下的类库, 是应用程序默认的类加载器.
父加载器为ExtClassLoader.

3.5. 用户自定义类加载器

为什么要自定义类加载器?
  • 修改类加载的方式.

  • 隔离加载类.

  • 扩展加载源.

  • 防止源码泄露.

实现自定义类加载器的几种方式
  1. 继承 ClassLoader 类, 覆盖 loadClass() 方法.

  2. 继承 URLClassLoader 类.

3.6. 双亲委派机制

加载某个类的class文件时, 把请求委托给父加载器处理.
如果父加载器无法加载, 则子加载器才会自己去加载.

优点
  • 避免类的重复加载.

  • 防止jdk核心类的篡改.

打破双亲委派机制的案例:
  • DriverManager属于JDK下的类, 但从线程上下文取到 AppClassLoader 去加载数据库厂商的JDBC Driver获取链接.

  • Tomcat使用WebAppClassLoader自己先尝试加载类, 加载不到再去找父类加载.

4. 类加载的过程

4.1. 加载

  1. 通过一个类的全限定名获取定义此类的二进制字节流.

  2. 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构.

  3. 在内存中生成一个代表这个类的 java.lang.Class 对象, 作为方法区这个类的各种数据的访问入口.

4.2. 链接

链接分为 验证, 准备, 解析 三个阶段.

验证

确保字节流符合当前虚拟机要求, 保证类加载的正确性.
验证文件格式/元数据/字节码/符号引用.

准备

为静态变量分配零值(null/0).
常量设置初始值.

解析

解析符号引用为直接引用.

4.3. 初始化

执行类构造器方法 clinit() , 该方法是由javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来.

触发类初始化的条件
  • 创建类的实例.

  • 访问类的静态变量, 或者对该变量赋值.

  • 调用类的静态方法.

  • 反射. Class.forName("FQCN")

  • 加载一个类的子类.

  • JVM启动时作为启动入口的类.

  • 通过MethodHandler解析的类.

5. JVM内存布局

jvm-memory

红色代表线程共享, 灰色代表线程私有.

5.1. PC

存储下一条要执行的指令的地址.
如果在执行的是native方法, 则值为undefined.

5.2. 虚拟机栈

每个线程在创建时都会创建一个虚拟机栈, 栈中的数据以栈帧的格式存在.
在这个线程上执行的每个方法都各自对应一个栈帧.
栈顶的栈帧对应的就是当前正在执行的方法, 如果该方法调用了其他方法, 则创建出一个新的栈帧并入栈.
方法执行完或抛出异常时当前栈帧会出栈.

栈帧组成
  • 局部变量表(方法内部局部变量字节码行号和变量名的映射关系.)

  • 操作数栈(在方法执行过程中保存计算结果的临时存储空间. 操作数栈的大小 max_stack 在编译时就会确定好, 保存在方法的Code属性中.)

  • 动态链接(指向运行时常量池的方法引用.)

  • 方法返回地址(存放调用该方法的pc寄存器的值.)

  • …​

线程请求分配的栈容量超过JVM允许的最大容量, JVM会抛出 StackOverflowError .
如果Java虚拟机栈在尝试扩展的时候申请不到足够的内存, 或者创建新的线程时没有足够的内存去创建对应的虚拟机栈, JVM会抛出 OutOfMemoryError .

StackOverflowError
    // java.lang.StackOverflowError
    private static void stackOverflow() {
        stackOverflow();
    }
OutOfMemoryError
    // java.lang.OutOfMemoryError: unable to create new native thread
    private static void createInfiniteThread() {
        while (true) {
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(100L);
                } catch (InterruptedException ignored) {
                }
            }).start();
        }
    }

5.3. 本地方法栈

管理本地方法的调用.

5.4. 方法区

存储类的结构信息, 运行时常量池, JIT编译后的代码等等.

5.5. 堆

存储对象实例.

5.5.1. OOM异常类型

  • Java heap space

  • GC overhead limit exceeded

  • PermGen space

  • Metaspace

  • Unable to create new native thread

  • Out of swap space

  • Requested array size exceeds VM limit

  • Kill process or sacrifice child

  • reason stack_trace_with_native_method

  • Direct Buffer Memory

  • Map failed

6. JDK常用工具

6.1. jcmd

打印Java进程所涉及的基本类, 线程和VM信息

jcmd <pid> <command> [args]

  • jcmd 9914 VM.system_properties 查看JVM进程properties

  • jcmd 9914 VM.uptime 查看JVM进程运行时间

  • jcmd 9914 VM.version 查看JVM版本

  • jcmd 9914 VM.command_line 查看JVM启动命令

  • jcmd 9914 VM.flags 查看JVM启动参数

  • jcmd 9914 Thread.print 查看JVM线程栈信息

  • jcmd 9914 GC.run 手动执行一次FullGC.

  • jcmd 9914 GC.heap_info 查看对内存区域占用.

  • jcmd 9914 GC.heap_dump dump内存.

6.2. jps

查看系统运行的所有Java进程的pid

jps选项:
  • -q 只打印JVM进程id.

  • -m 打印main方法执行参数.

  • -l 打印启动类全限定名或者启动jar包的文件路径.

  • -v 打印JVM启动参数.

6.3. jinfo

查看指定Java进程信息

jinfo [option] <pid>

  • jinfo -flags 1 查看pid为1的进程的所有启动参数

  • jinfo -flag UseCompressedOops 9914 查看指定参数

6.4. jstack

jstack <pid>

查看指定Java进程中每个线程栈信息

6.5. jstat

jstat [option] <pid> [interval] [times]

查看指定进程的内存使用及GC概况. 如: jstat -gcutil 6451 1000 每隔1秒输出进程6451的概况

各列含义
  • S0 第一个survivor区容量使用率

  • S1 第二个survivor区容量使用率

  • E Eden区容量使用率

  • O Old区容量使用率

  • M Metaspace区容量使用率

  • YGC young gc次数

  • YGCT young gc总耗时

  • FGC full gc次数

  • FGCT full gc总耗时

  • GCT gc总耗时

6.6. jmap

查看指定进程的堆内存使用情况.

jmap [option] <pid>

jmap选项:
  • -heap 查看堆内存的配置和使用信息.

  • -histo 查看各个类实例的数量和占用的内存大小.

  • -dump:format=b;file=xxx.hprof dump堆内存.

6.7. javap

查看class字节码文件

javap <class file>

  • -c: 反编译class

  • -p: 显示 private 方法和字段

  • -v: 显示详细信息

  • -s: 显示类型签名

  • -l: 输出行号和本地变量表

  • -sysinfo: 显示类的系统信息

7. JVM参数

7.1. 参数分类

JVM主要接受两类标志: boolean类和赋值类参数.

  • boolean类:
    -XX:+FLAG_NAME.
    如: -XX:+UseCompressedOops 开启64位JVM中的对象引用压缩,-XX:-UseCompressedOops 关闭压缩.

  • 赋值类: -XX:+FLAG_NAME=VALUE.
    -XX:AutoBoxCacheMax=20000.

  • 简写类.
    -Xms -Xmx -Xmn -Xss

查看所有可选标志: java -XX:+PrintFlagsFinal -version

7.2. 常用启动参数

GC收集器选择类
  • -XX:+UseSerialGC 使用Serial+Serial Old组合回收新生代和老年代.

  • -XX:+UseParNewGC 使用ParNew+Serial Old组合回收新生代和老年代.

  • -XX:+UseConcMarkSweepGC 使用ParNew+CMS组合回收新生代和老年代, 当出现 Concurrent Mode Failure 后使用Serial Old回收老年代.

  • -XX:+UseParallelGC 使用Parallel Scavenge+Serial Old组合回收新生代和老年代.

  • -XX:+UseParallelOldGC 使用Parallel Scavenge+Parallel Old组合回收新生代和老年代.

  • -XX:+UseG1GC 使用G1回收堆内存.

  • -XX:+UseZGC 使用ZGC回收堆内存.

  • -XX:+UseShenandoahGC 使用ShenandoahGC回收堆内存, 只能在OpenJDK12及以上版本中使用.

Java8及以下GC日志输出类
  • -XX:+PrintGC 输出GC简要日志.

  • -XX:+PrintGCDetails 输出gc详细日志.

  • -XX:+PrintHeapAtGC 输出GC前后堆和方法区容量大小.

  • -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime 输出GC与用户线程并发时间以及GC停顿时间.

  • -XX:+PrintAdaptiveSizePolicy 查看JVM堆各个分代大小自动调节信息.

  • -XX:+PrintTenuringDistribution 查看GC后剩余对象的年龄分布信息.

  • -Xloggc:<FILENAME> -XX:+UseGCLogfileRotation -XX:NumberOfGCLogfiles=N -XX:GCLogfileSize=N gc日志写入指定文件并切割.

Java9及以上GC日志输出类 -Xlog[:[what][:[output][:[decorators][:output-options[,…​]]]]]
  • -Xlog:gc 输出GC简要日志.

  • -Xlog:gc* 输出GC详细日志.

  • -Xlog:gc+heap=debug 输出GC前后堆和方法区容量大小.

  • -Xlog:safepoint 输出GC与用户线程并发时间以及GC停顿时间.

  • -Xlog:gc+ergo*=trace 查看JVM堆各个分代大小自动调节信息.

  • -Xlog:gc+age=trace 查看GC后剩余对象的年龄分布信息.

  • -Xlog:gc*:file=<file>::filecount=<count>,filesize=<sizekb> gc日志写入指定文件并切割.

GC性能类
  • -XX:SurvivorRatio=8 新生代中 Eden 区和 Survivor 区的比例, 默认为8, 及 8:1:1 .

  • -XX:PretenureSizeThreshold=0 直接晋升到老年代的对象大小, 大于这个参数的对象将直接在老年代上分配.

  • -XX:MaxTenuringThreshold=15 晋升到老年代的对象年龄, 每个对象在 Minor GC 后年龄加一, 当年龄超过这个参数后会进入老年代.

  • -XX:+UseAdaptiveSizePolicy (不推荐) 动态调整堆中各个区域的大小以及老年代的年龄.

  • -XX:ParallelGCThreads=8 设置并行GC时进行内存回收的线程数.

  • -XX:GCTimeRatio=N 设置应用运行时间占比: \$"Throughput"="GCTimeRatio"/(1+"GCTimeRatio")\$

  • -XX:MaxGCPauseMillis=N 设定应用可承受的最大停顿时间.一般设置为200, 如果设置的值过小, 会导致老年代非常小, 从而频繁进行 Full GC .

  • -XX:CMSInitiatingOccupancyFraction=68 设置CMS收集器在老年代空间被使用多少后触发垃圾回收, 默认为68%.

  • -XX:+UseCMSCompactAtFullCollection 设置CMS在 Full GC 后是否进行内存碎片整理. [Java9后废弃]

  • -XX:CMSFullGCsBeforeCompaction=0 设置CMS在多少次 Full GC 后进行内存碎片整理. [Java9后废弃]

  • -XX:G1HeapRegionSize=0 设置G1 Region大小.

  • -XX:G1NewSizePercent=5 设置G1新生代最小值, 默认为5%.

  • -XX:G1MaxNewSizePercent=60 设置G1新生代最大值, 默认为60%.

  • -XX:InitiatingHeapOccupancyPercent=45 设置触发标记周期的堆(old+humongous)占用阈值, 默认为45%.

8. 内存分配策略

8.1. 内存分配方式

  • 指针碰撞: 所有被使用的内存放一边, 空闲的内存放一边, 维护一个指针标识两部分内存的分界线, 使用标记-整理的垃圾回收器会使用指针碰撞的分配方式.

  • 空闲列表: 虚拟机维护一个列表, 记录哪些内存块是可用的, 哪些是不可用的, 使用标记-清除的垃圾回收器会使用空闲列表的分配方式.

8.2. 对象优先在Eden区分配

大多数情况下, 对象在栈上或者新生代Eden区中分配.
当Eden区没有足够空间进行分配时, 虚拟机将发起一次 Minor GC .

8.3. 大对象直接进入老年代

需要大量连续内存空间的Java对象(如数组), 直接在老年代分配.
虚拟机提供了 -XX:PretenureSizeThreshold 参数指定大于该参数值的对象直接在老年代分配.

8.4. 长期存活的对象进入老年代

对象如果在 Minor GC 后仍然存活, 并能被Survivor区容纳, 则移动到Survivor区, 年龄加1. 年龄超过 MaxTenuringThreshold 参数后, 下次 Minor GC 时会进入老年代.

8.5. 老年代空间分配担保

Minor GC 之前, 虚拟机会先检查老年代最大可用的连续空间是否大于等于新生代所有对象总空间或者大于等于历史晋升的平均大小, 如果大于则进行 Minor GC , 否则将直接进行 Full FC .

9. 垃圾收集算法

9.1. 复制

将内存按容量划分为大小相等的两块, 每次只用其中的一块.
当某一块的内存用完了, 就将还存活的的对象复制到另外一块上, 然后再把已使用过的内存空间一次清理掉.

优点
  • 为对象分配内存时不需要考虑内存碎片的问题.

缺点
  • 可用内存只有一半.

  • 如果对象的存活率高, 则拷贝对象的操作开销比较大.

使用场景
  • Serial/ParNew收集器使用复制算法收集新生代.

9.2. 标记整理

让所有存活的对象向内存空间一端移动, 然后直接清理掉边界以外的内存.

优点
  • 没有内存碎片

缺点
  • 移动对象的开销大.

使用场景
  • Serial Old/Parallel Old使用标记-整理算法收集老年代.

9.3. 标记清除

首先标记出所有需要回收的对象, 在标记完成后统一回收所有被标记的对象.

优点
  • 相对于复制算法节省了内存空间.

缺点
  • 执行效率不稳定, 如果内存中大量对象需要被回收, 则必须进行大量标记和清除的动作.

  • 导致内存碎片化.

使用场景
  • CMS使用标记清除算法回收老年代.

10. GC分类

  • Minor GC/Young GC: 新生代的收集.

  • Major GC/Old GC: 老年代的收集.(CMS)

  • Mixed GC: 收集整个新生代和部分老年代的过程.(G1)

  • Full GC: 整个Java堆和方法区的收集.

11. GC Root

  • 虚拟机栈和本地方法栈中本地变量表引用的对象.

  • 方法区中静态属性引用的对象.

  • 方法区中常量引用的对象.

  • 虚拟机内部的类引用的对象, 比如JDK官方的类Class对象或者类加载器.

  • 被同步锁持有的对象.

12. 垃圾收集器

垃圾收集分两步: 在为对象分配内存时遇到内存不足, 查找不再使用的对象, 然后释放这些对象所在的内存.

由于对象的生存时间不同, 所有的垃圾收集器都采用分代收集的方式.
堆内存被划分为 新生代 (Young Generation)和 老年代 (Old Generation或Tenured Generation), 默认空间占比 1:2. 新生代又被分为一个Eden区和两个Survivor区.
默认空间占比 8:1:1.

  • 对象首先尝试在栈上分配, 栈空间不够时在新生代Eden区分配.

  • Eden区填满时 , 垃圾收集器会暂停所有应用线程回收新生代(垃圾收集时所有应用线程停止运行所产生的停顿称为 STW (stop-the-world)).此时不再使用的对象会被回收, 仍在使用的对象会移动到Survivor区或老年代, 这一过程称为 Minor GC . 由于所有的存活对象都被移走, 此时相当于在新生代做了一次整理.

  • 单独回收老年代的过程称为 Major GC . (CMS)

  • 回收新生代和一部分老年代的过程称为 Mixed GC .(G1)

  • 对象不断移动到老年代, 等到 老年代空间占满 , JVM会回收整个堆, 这一过程称为 Full GC .

12.1. Serial

Serial垃圾收集器使用 单线程 回收内存, 垃圾回收时会暂停所有的用户线程.
使用 -XX:+UseSerialGC 启用Serial垃圾收集器.

  • 新生代 Serial 使用复制算法, GC时会暂停所有用户线程.

  • 老年代 Serial Old 使用标记-整理算法, GC时暂停所有用户线程.

java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xmx500m -XX:+UseSerialGC GCLogAnalysis
2020-10-27T20:27:43.434-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.434-0800: [DefNew: 69870K->8703K(78656K), 0.0146897 secs] 69870K->23463K(253440K), 0.0147228 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] (1)
2020-10-27T20:27:43.475-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.475-0800: [DefNew: 78655K->8704K(78656K), 0.0219163 secs] 93415K->43153K(253440K), 0.0219731 secs] [Times: user=0.01 sys=0.01, real=0.02 secs]
2020-10-27T20:27:43.518-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.518-0800: [DefNew: 78656K->8704K(78656K), 0.0268356 secs] 113105K->68112K(253440K), 0.0268698 secs] [Times: user=0.02 sys=0.01, real=0.03 secs]
2020-10-27T20:27:43.573-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.573-0800: [DefNew: 78656K->8703K(78656K), 0.0283694 secs] 138064K->86737K(253440K), 0.0284128 secs] [Times: user=0.02 sys=0.01, real=0.03 secs]
2020-10-27T20:27:43.633-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.633-0800: [DefNew: 78655K->8704K(78656K), 0.0204895 secs] 156689K->110372K(253440K), 0.0205465 secs] [Times: user=0.01 sys=0.01, real=0.02 secs]
2020-10-27T20:27:43.675-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.675-0800: [DefNew: 78656K->8698K(78656K), 0.0295495 secs] 180324K->135983K(253440K), 0.0295976 secs] [Times: user=0.02 sys=0.01, real=0.03 secs]
2020-10-27T20:27:43.719-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.719-0800: [DefNew: 78650K->8704K(78656K), 0.0269409 secs] 205935K->161503K(253440K), 0.0269808 secs] [Times: user=0.02 sys=0.01, real=0.03 secs]
2020-10-27T20:27:43.758-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.758-0800: [DefNew: 78553K->8703K(78656K), 0.0247593 secs]2020-10-27T20:27:43.783-0800: [Tenured: 174929K->161954K(174976K), 0.0382564 secs] 231353K->161954K(253632K), [Metaspace: 5478K->5478K(1056768K)], 0.0632989 secs] [Times: user=0.05 sys=0.01, real=0.07 secs] (2)
2020-10-27T20:27:43.858-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.858-0800: [DefNew: 108096K->13439K(121536K), 0.0261365 secs] 270050K->199412K(391464K), 0.0261749 secs] [Times: user=0.02 sys=0.01, real=0.03 secs]
2020-10-27T20:27:43.910-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.910-0800: [DefNew: 121535K->13430K(121536K), 0.0407543 secs] 307508K->233687K(391464K), 0.0407952 secs] [Times: user=0.05 sys=0.03, real=0.04 secs]
2020-10-27T20:27:43.979-0800: [GC (Allocation Failure) 2020-10-27T20:27:43.979-0800: [DefNew: 121526K->13439K(121536K), 0.0282363 secs] 341783K->266291K(391464K), 0.0282747 secs] [Times: user=0.01 sys=0.01, real=0.03 secs]
2020-10-27T20:27:44.033-0800: [GC (Allocation Failure) 2020-10-27T20:27:44.033-0800: [DefNew: 121417K->13432K(121536K), 0.0318451 secs]2020-10-27T20:27:44.065-0800: [Tenured: 290446K->241497K(290448K), 0.0549298 secs] 374268K->241497K(411984K), [Metaspace: 5478K->5478K(1056768K)], 0.0869765 secs] [Times: user=0.07 sys=0.02, real=0.09 secs]
2020-10-27T20:27:44.137-0800: [GC (Allocation Failure) 2020-10-27T20:27:44.137-0800: [DefNew: 136419K->17022K(153600K), 0.0244343 secs] 377916K->287817K(494976K), 0.0244742 secs] [Times: user=0.01 sys=0.01, real=0.03 secs]
2020-10-27T20:27:44.188-0800: [GC (Allocation Failure) 2020-10-27T20:27:44.188-0800: [DefNew: 153167K->17023K(153600K), 0.0434442 secs] 423962K->328060K(494976K), 0.0435019 secs] [Times: user=0.02 sys=0.02, real=0.05 secs]
2020-10-27T20:27:44.249-0800: [GC (Allocation Failure) 2020-10-27T20:27:44.249-0800: [DefNew: 153599K->153599K(153600K), 0.0000215 secs]2020-10-27T20:27:44.249-0800: [Tenured: 311036K->293415K(341376K), 0.0753151 secs] 464636K->293415K(494976K), [Metaspace: 5478K->5478K(1056768K)], 0.0754695 secs] [Times: user=0.07 sys=0.00, real=0.08 secs]
2020-10-27T20:27:44.351-0800: [GC (Allocation Failure) 2020-10-27T20:27:44.351-0800: [DefNew: 136576K->17022K(153600K), 0.0170228 secs] 429991K->341321K(494976K), 0.0170677 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2020-10-27T20:27:44.385-0800: [GC (Allocation Failure) 2020-10-27T20:27:44.385-0800: [DefNew: 153598K->153598K(153600K), 0.0000162 secs]2020-10-27T20:27:44.385-0800: [Tenured: 324298K->300756K(341376K), 0.0832796 secs] 477897K->300756K(494976K), [Metaspace: 5478K->5478K(1056768K)], 0.0833456 secs] [Times: user=0.08 sys=0.00, real=0.08 secs]
Heap
 def new generation   total 153600K, used 5917K [0x00000007a0c00000, 0x00000007ab2a0000, 0x00000007ab2a0000)
  eden space 136576K,   4% used [0x00000007a0c00000, 0x00000007a11c75b0, 0x00000007a9160000)
  from space 17024K,   0% used [0x00000007aa200000, 0x00000007aa200000, 0x00000007ab2a0000)
  to   space 17024K,   0% used [0x00000007a9160000, 0x00000007a9160000, 0x00000007aa200000)
 tenured generation   total 341376K, used 300756K [0x00000007ab2a0000, 0x00000007c0000000, 0x00000007c0000000)
   the space 341376K,  88% used [0x00000007ab2a0000, 0x00000007bd8553b0, 0x00000007bd855400, 0x00000007c0000000)
 Metaspace       used 5492K, capacity 5958K, committed 6016K, reserved 1056768K
  class space    used 595K, capacity 626K, committed 640K, reserved 1048576K
1 本次为YoungGC, Young区总大小为78656K, 整个堆总大小为253440K, GC后Young区使用量从69870K降到了8703K, 整个堆使用量从69870K降到了23463K.
2 本次为FullGC, Young区总大小为78656K, Old区总大小为174976K, 整个堆总大小为253632K, GC后Young区使用量从78553K降到了8703K, Old区使用量从174929K降到了161954K, 整个堆使用量从231353K降到了161954K.

12.2. Parallel

JDK8默认收集器.

Parallel垃圾收集器使用 多线程 并行回收内存, 垃圾回收时会暂停所有的用户线程.
使用 -XX:+UseParallelGC 启用Parallel垃圾收集器.

  • 新生代 Parallel Scavenge 使用复制算法, GC时暂停所有用户线程.

  • 老年代 Parallel Old 使用标记-整理算法, GC时暂停所有用户线程.

Parallel收集器参数
  • -XX:MaxGCPauseMills 收集器将尽力保证内存回收花费的时间不包括这个设定的值.

  • -XX:GCTimeRatio 垃圾收集时间占总时间的占比. 默认99.

  • -XX:+UseAdaptiveSizePolicy JVM根据当前系统的运行信息自动调节 -Xmn/-XX:SurvivorRatio/-XX:PretenureSizeThreshold

java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xmx500m -XX:+UseParallelGC GCLogAnalysis
2020-10-27T20:31:42.234-0800: [GC (Allocation Failure) [PSYoungGen: 65536K->10732K(76288K)] 65536K->27190K(251392K), 0.0121700 secs] [Times: user=0.01 sys=0.05, real=0.01 secs] (1)
2020-10-27T20:31:42.274-0800: [GC (Allocation Failure) [PSYoungGen: 76208K->10749K(141824K)] 92666K->51153K(316928K), 0.0158102 secs] [Times: user=0.02 sys=0.07, real=0.02 secs]
2020-10-27T20:31:42.359-0800: [GC (Allocation Failure) [PSYoungGen: 141821K->10751K(141824K)] 182225K->92844K(316928K), 0.0271800 secs] [Times: user=0.04 sys=0.11, real=0.03 secs]
2020-10-27T20:31:42.425-0800: [GC (Allocation Failure) [PSYoungGen: 141823K->10751K(159744K)] 223916K->140207K(334848K), 0.0303295 secs] [Times: user=0.04 sys=0.13, real=0.03 secs]
2020-10-27T20:31:42.455-0800: [Full GC (Ergonomics) [PSYoungGen: 10751K->0K(159744K)] [ParOldGen: 129455K->126304K(263680K)] 140207K->126304K(423424K), [Metaspace: 5486K->5486K(1056768K)], 0.0292847 secs] [Times: user=0.13 sys=0.00, real=0.03 secs] (2)
2020-10-27T20:31:42.527-0800: [GC (Allocation Failure) [PSYoungGen: 148992K->10751K(159744K)] 275296K->177754K(423424K), 0.0274459 secs] [Times: user=0.05 sys=0.10, real=0.03 secs]
2020-10-27T20:31:42.591-0800: [GC (Allocation Failure) [PSYoungGen: 159743K->10743K(67584K)] 326746K->226687K(331264K), 0.0304457 secs] [Times: user=0.05 sys=0.11, real=0.03 secs]
2020-10-27T20:31:42.621-0800: [Full GC (Ergonomics) [PSYoungGen: 10743K->0K(67584K)] [ParOldGen: 215944K->188313K(341504K)] 226687K->188313K(409088K), [Metaspace: 5486K->5486K(1056768K)], 0.0271394 secs] [Times: user=0.12 sys=0.00, real=0.02 secs]
2020-10-27T20:31:42.669-0800: [GC (Allocation Failure) [PSYoungGen: 56258K->16332K(113664K)] 244572K->204646K(455168K), 0.0024383 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2020-10-27T20:31:42.687-0800: [GC (Allocation Failure) [PSYoungGen: 73164K->32564K(113664K)] 261478K->220878K(455168K), 0.0075769 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]
2020-10-27T20:31:42.709-0800: [GC (Allocation Failure) [PSYoungGen: 88659K->47677K(113664K)] 276972K->235991K(455168K), 0.0078832 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
2020-10-27T20:31:42.736-0800: [GC (Allocation Failure) [PSYoungGen: 104509K->36972K(113664K)] 292823K->254875K(455168K), 0.0100647 secs] [Times: user=0.05 sys=0.01, real=0.01 secs]
2020-10-27T20:31:42.772-0800: [GC (Allocation Failure) [PSYoungGen: 93415K->18147K(113664K)] 311317K->271321K(455168K), 0.0249421 secs] [Times: user=0.03 sys=0.09, real=0.02 secs]
2020-10-27T20:31:42.806-0800: [GC (Allocation Failure) [PSYoungGen: 74947K->19445K(113664K)] 328122K->289146K(455168K), 0.0093222 secs] [Times: user=0.02 sys=0.02, real=0.01 secs]
2020-10-27T20:31:42.831-0800: [GC (Allocation Failure) [PSYoungGen: 76277K->17627K(113664K)] 345978K->305371K(455168K), 0.0089946 secs] [Times: user=0.02 sys=0.03, real=0.01 secs]
2020-10-27T20:31:42.854-0800: [GC (Allocation Failure) [PSYoungGen: 74459K->16959K(113664K)] 362203K->321770K(455168K), 0.0116318 secs] [Times: user=0.03 sys=0.04, real=0.01 secs]
2020-10-27T20:31:42.866-0800: [Full GC (Ergonomics) [PSYoungGen: 16959K->0K(113664K)] [ParOldGen: 304811K->241616K(341504K)] 321770K->241616K(455168K), [Metaspace: 5486K->5486K(1056768K)], 0.0484392 secs] [Times: user=0.23 sys=0.01, real=0.05 secs]
2020-10-27T20:31:42.930-0800: [GC (Allocation Failure) [PSYoungGen: 56669K->22742K(113664K)] 298286K->264359K(455168K), 0.0036387 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2020-10-27T20:31:42.943-0800: [GC (Allocation Failure) [PSYoungGen: 79574K->18280K(113664K)] 321191K->281473K(455168K), 0.0054439 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
2020-10-27T20:31:42.967-0800: [GC (Allocation Failure) [PSYoungGen: 75074K->20044K(113664K)] 338267K->300845K(455168K), 0.0054350 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
2020-10-27T20:31:42.989-0800: [GC (Allocation Failure) [PSYoungGen: 76850K->23313K(113664K)] 357652K->322762K(455168K), 0.0099082 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
2020-10-27T20:31:43.008-0800: [GC (Allocation Failure) [PSYoungGen: 80105K->16903K(113664K)] 379553K->338875K(455168K), 0.0191813 secs] [Times: user=0.05 sys=0.06, real=0.02 secs]
2020-10-27T20:31:43.027-0800: [Full GC (Ergonomics) [PSYoungGen: 16903K->0K(113664K)] [ParOldGen: 321971K->272257K(341504K)] 338875K->272257K(455168K), [Metaspace: 5486K->5486K(1056768K)], 0.0505424 secs] [Times: user=0.24 sys=0.00, real=0.05 secs]
2020-10-27T20:31:43.096-0800: [GC (Allocation Failure) [PSYoungGen: 56243K->22017K(113664K)] 328500K->294275K(455168K), 0.0045945 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
2020-10-27T20:31:43.110-0800: [GC (Allocation Failure) [PSYoungGen: 78849K->18143K(113664K)] 351107K->311726K(455168K), 0.0061414 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
2020-10-27T20:31:43.130-0800: [GC (Allocation Failure) [PSYoungGen: 74971K->16293K(113664K)] 368555K->325547K(455168K), 0.0059918 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
2020-10-27T20:31:43.136-0800: [Full GC (Ergonomics) [PSYoungGen: 16293K->0K(113664K)] [ParOldGen: 309254K->287559K(341504K)] 325547K->287559K(455168K), [Metaspace: 5486K->5486K(1056768K)], 0.0510250 secs] [Times: user=0.24 sys=0.00, real=0.05 secs]
Heap
 PSYoungGen      total 113664K, used 28277K [0x00000007b5980000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 56832K, 49% used [0x00000007b5980000,0x00000007b751d4e0,0x00000007b9100000)
  from space 56832K, 0% used [0x00000007bc880000,0x00000007bc880000,0x00000007c0000000)
  to   space 56832K, 0% used [0x00000007b9100000,0x00000007b9100000,0x00000007bc880000)
 ParOldGen       total 341504K, used 287559K [0x00000007a0c00000, 0x00000007b5980000, 0x00000007b5980000)
  object space 341504K, 84% used [0x00000007a0c00000,0x00000007b24d1cb0,0x00000007b5980000)
 Metaspace       used 5499K, capacity 5958K, committed 6016K, reserved 1056768K
  class space    used 595K, capacity 626K, committed 640K, reserved 1048576K
1 本次为YoungGC, Young区总大小为76288K, 整个堆总大小为251392K, GC后Young区使用量从65536K降到了10732K, 整个堆使用量从65536K降到了27190K.
2 本次为FullGC, Young区总大小为159744K, Old区总大小为263680K, 整个堆总大小为423424K, GC后Young区使用量从10751K降到了0K, Old区使用量从129455K降到了126304K, 整个堆使用量从140207K降到了126304K.

12.3. ParNew

Serial垃圾收集器使用 多线程 回收 新生代内存 (线程数等于CPU数), 垃圾回收时会暂停所有的应用线程.
使用 -XX:+UseParNewGC 启用ParNew垃圾收集器.
通常与CMS搭配使用.

  • 新生代 ParNew 使用复制算法, GC时会暂停所有用户线程.

12.4. CMS

JDK14被移除.

CMS使用多线程并发回收老年代内存, 使用 -XX:+UseConcMarkSweepGC 启用CMS收集器.

CMS回收流程
  1. 初始标记: 单线程标记一下 GC Roots 对象.

  2. 并发标记: 从 GC Roots 对象开始遍历整个对象图.

  3. 重新标记: 修正并发标记期间, 因用户程序继续运作而导致标记变动的那部分对象的标记记录.

  4. 并发清除: 清除那些被标记为死亡的对象.
    阶段1和阶段3会暂停所有用户线程, 阶段2和阶段4GC线程可以和用户线程并发执行.

CMS回收并发线程数默认是 \$("nCPU"+3)/4\$ .

CMS参数
  • -XX:CMSInitiatingOccupancyFraction 老年代占用超过这个阈值后会触发CMS回收内存.

  • -XX:+UseCMSCompactAtFullCollection 在Full GC时开启内存碎片的合并.

  • -XX:CMSFullGCsBeforeCompaction 在执行指定次数不整理空间的Full FC后, 在下一次Full GC前整理内存碎片.

  • -XX:+CMSScavengeBeforeRemark 在CMS重新标记阶段前触发 Young GC .

CMS退化成SerialOld收集器触发FullGC的两个诱因
  • YoungGC导致Young区对象晋升到Old区, 但是Old区没有足够容量收纳晋升过来的对象, 此时触发FullGC, 在GC日志中标识为 promotion failed .

    • 解决办法: 增大Young区大小.

  • CMS并发标记阶段应用线程会产生一部分新的可回收对象( 浮动垃圾 ), 且分配新的对象内存的时候可能因为内存不足触发FullGC, 在GC日志中标识为 concurrent mode failure .

    • 解决办法: 让Old区提前回收(-XX:CMSInitiatingOccupancyFraction), 或者每次CMS GC后整理下内存(-XX:+UseCMSCompactAtFullCollection).

java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xmx500m -XX:+UseConcMarkSweepGC GCLogAnalysis
2020-10-27T20:39:17.104-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.104-0800: [ParNew: 69952K->8698K(78656K), 0.0093642 secs] 69952K->22083K(253440K), 0.0094129 secs] [Times: user=0.02 sys=0.04, real=0.01 secs] (1)
2020-10-27T20:39:17.141-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.141-0800: [ParNew: 78650K->8698K(78656K), 0.0087938 secs] 92035K->39298K(253440K), 0.0088282 secs] [Times: user=0.02 sys=0.03, real=0.01 secs]
2020-10-27T20:39:17.174-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.174-0800: [ParNew: 78650K->8703K(78656K), 0.0225999 secs] 109250K->60847K(253440K), 0.0227016 secs] [Times: user=0.12 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.218-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.218-0800: [ParNew: 78324K->8704K(78656K), 0.0192211 secs] 130467K->82592K(253440K), 0.0192581 secs] [Times: user=0.11 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.267-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.267-0800: [ParNew: 78656K->8702K(78656K), 0.0162077 secs] 152544K->107316K(253440K), 0.0162478 secs] [Times: user=0.10 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.283-0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 98614K(174784K)] 108733K(253440K), 0.0001820 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] (2)
2020-10-27T20:39:17.283-0800: [CMS-concurrent-mark-start] (3)
2020-10-27T20:39:17.285-0800: [CMS-concurrent-mark: 0.002/0.002 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.285-0800: [CMS-concurrent-preclean-start] (4)
2020-10-27T20:39:17.286-0800: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.286-0800: [CMS-concurrent-abortable-preclean-start] (5)
2020-10-27T20:39:17.304-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.304-0800: [ParNew: 78654K->8700K(78656K), 0.0146260 secs] 177268K->129456K(253440K), 0.0146893 secs] [Times: user=0.09 sys=0.01, real=0.01 secs]
2020-10-27T20:39:17.340-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.340-0800: [ParNew: 78652K->8698K(78656K), 0.0170268 secs] 199408K->150863K(253440K), 0.0170998 secs] [Times: user=0.11 sys=0.00, real=0.01 secs]
2020-10-27T20:39:17.372-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.372-0800: [ParNew: 78397K->8702K(78656K), 0.0147147 secs] 220562K->172577K(253440K), 0.0147697 secs] [Times: user=0.09 sys=0.00, real=0.01 secs]
2020-10-27T20:39:17.407-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.407-0800: [ParNew: 78368K->8702K(78656K), 0.0148902 secs] 242243K->195716K(266244K), 0.0149562 secs] [Times: user=0.09 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.443-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.443-0800: [ParNew: 78654K->8703K(78656K), 0.0261878 secs] 265668K->223486K(294104K), 0.0262856 secs] [Times: user=0.15 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.490-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.491-0800: [ParNew: 78655K->8703K(78656K), 0.0269818 secs] 293438K->244642K(315344K), 0.0271319 secs] [Times: user=0.13 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.530-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.530-0800: [ParNew: 78467K->8700K(78656K), 0.0222884 secs] 314407K->269319K(339980K), 0.0224062 secs] [Times: user=0.14 sys=0.02, real=0.02 secs]
2020-10-27T20:39:17.572-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.572-0800: [ParNew: 78652K->8697K(78656K), 0.0280387 secs] 339271K->293670K(364248K), 0.0281057 secs] [Times: user=0.17 sys=0.02, real=0.03 secs]
2020-10-27T20:39:17.611-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.611-0800: [ParNew: 78338K->8702K(78656K), 0.0207492 secs] 363311K->316144K(386844K), 0.0208035 secs] [Times: user=0.12 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.661-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.661-0800: [ParNew: 78654K->8703K(78656K), 0.0168668 secs] 386096K->337852K(408460K), 0.0169848 secs] [Times: user=0.11 sys=0.00, real=0.01 secs]
2020-10-27T20:39:17.679-0800: [CMS-concurrent-abortable-preclean: 0.016/0.393 secs] [Times: user=1.55 sys=0.15, real=0.39 secs]
2020-10-27T20:39:17.679-0800: [GC (CMS Final Remark) [YG occupancy: 17167 K (78656 K)]2020-10-27T20:39:17.679-0800: [Rescan (parallel) , 0.0009628 secs]2020-10-27T20:39:17.680-0800: [weak refs processing, 0.0000274 secs]2020-10-27T20:39:17.680-0800: [class unloading, 0.0008790 secs]2020-10-27T20:39:17.681-0800: [scrub symbol table, 0.0008922 secs]2020-10-27T20:39:17.682-0800: [scrub string table, 0.0002329 secs][1 CMS-remark: 329149K(329804K)] 346317K(408460K), 0.0031208 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] (6)
2020-10-27T20:39:17.682-0800: [CMS-concurrent-sweep-start] (7)
2020-10-27T20:39:17.683-0800: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.683-0800: [CMS-concurrent-reset-start] (8)
2020-10-27T20:39:17.684-0800: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.700-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.701-0800: [ParNew: 78302K->78302K(78656K), 0.0000177 secs]2020-10-27T20:39:17.701-0800: [CMS: 318914K->241056K(341376K), 0.0705879 secs] 397217K->241056K(420032K), [Metaspace: 5484K->5484K(1056768K)], 0.0708164 secs] [Times: user=0.09 sys=0.00, real=0.07 secs]
2020-10-27T20:39:17.772-0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 241056K(341376K)] 241603K(494976K), 0.0002567 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.772-0800: [CMS-concurrent-mark-start]
2020-10-27T20:39:17.774-0800: [CMS-concurrent-mark: 0.002/0.002 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.774-0800: [CMS-concurrent-preclean-start]
2020-10-27T20:39:17.775-0800: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.775-0800: [CMS-concurrent-abortable-preclean-start]
2020-10-27T20:39:17.827-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.827-0800: [ParNew: 136576K->17017K(153600K), 0.0153041 secs] 377632K->289227K(494976K), 0.0153775 secs] [Times: user=0.10 sys=0.01, real=0.02 secs]
2020-10-27T20:39:17.871-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.871-0800: [ParNew: 153593K->17022K(153600K), 0.0170277 secs] 425803K->336041K(494976K), 0.0170661 secs] [Times: user=0.12 sys=0.01, real=0.01 secs]
2020-10-27T20:39:17.891-0800: [CMS-concurrent-abortable-preclean: 0.005/0.116 secs] [Times: user=0.36 sys=0.05, real=0.12 secs]
2020-10-27T20:39:17.891-0800: [GC (CMS Final Remark) [YG occupancy: 22623 K (153600 K)]2020-10-27T20:39:17.891-0800: [Rescan (parallel) , 0.0016173 secs]2020-10-27T20:39:17.893-0800: [weak refs processing, 0.0000399 secs]2020-10-27T20:39:17.893-0800: [class unloading, 0.0023604 secs]2020-10-27T20:39:17.896-0800: [scrub symbol table, 0.0010799 secs]2020-10-27T20:39:17.897-0800: [scrub string table, 0.0002871 secs][1 CMS-remark: 319019K(341376K)] 341642K(494976K), 0.0056143 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.897-0800: [CMS-concurrent-sweep-start]
2020-10-27T20:39:17.899-0800: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.899-0800: [CMS-concurrent-reset-start]
2020-10-27T20:39:17.899-0800: [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.920-0800: [GC (Allocation Failure) 2020-10-27T20:39:17.920-0800: [ParNew: 153598K->153598K(153600K), 0.0000383 secs]2020-10-27T20:39:17.920-0800: [CMS: 318202K->283658K(341376K), 0.0618453 secs] 471801K->283658K(494976K), [Metaspace: 5484K->5484K(1056768K)], 0.0619482 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
2020-10-27T20:39:17.982-0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 283658K(341376K)] 286702K(494976K), 0.0002134 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.982-0800: [CMS-concurrent-mark-start]
2020-10-27T20:39:17.984-0800: [CMS-concurrent-mark: 0.002/0.002 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.984-0800: [CMS-concurrent-preclean-start]
2020-10-27T20:39:17.985-0800: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:17.985-0800: [CMS-concurrent-abortable-preclean-start]
2020-10-27T20:39:18.007-0800: [GC (Allocation Failure) 2020-10-27T20:39:18.007-0800: [ParNew: 136576K->17023K(153600K), 0.0083199 secs] 420234K->331289K(494976K), 0.0083692 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
2020-10-27T20:39:18.017-0800: [CMS-concurrent-abortable-preclean: 0.002/0.032 secs] [Times: user=0.08 sys=0.00, real=0.03 secs]
2020-10-27T20:39:18.017-0800: [GC (CMS Final Remark) [YG occupancy: 29614 K (153600 K)]2020-10-27T20:39:18.017-0800: [Rescan (parallel) , 0.0004149 secs]2020-10-27T20:39:18.017-0800: [weak refs processing, 0.0000117 secs]2020-10-27T20:39:18.017-0800: [class unloading, 0.0009162 secs]2020-10-27T20:39:18.018-0800: [scrub symbol table, 0.0007536 secs]2020-10-27T20:39:18.019-0800: [scrub string table, 0.0001732 secs][1 CMS-remark: 314265K(341376K)] 343879K(494976K), 0.0023383 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:18.019-0800: [CMS-concurrent-sweep-start]
2020-10-27T20:39:18.020-0800: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
2020-10-27T20:39:18.020-0800: [CMS-concurrent-reset-start]
2020-10-27T20:39:18.020-0800: [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:18.040-0800: [GC (Allocation Failure) 2020-10-27T20:39:18.040-0800: [ParNew: 153599K->153599K(153600K), 0.0000214 secs]2020-10-27T20:39:18.040-0800: [CMS: 314265K->309716K(341376K), 0.0750800 secs] 467865K->309716K(494976K), [Metaspace: 5484K->5484K(1056768K)], 0.0751629 secs] [Times: user=0.08 sys=0.00, real=0.07 secs]
2020-10-27T20:39:18.116-0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 309716K(341376K)] 312690K(494976K), 0.0002100 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2020-10-27T20:39:18.116-0800: [CMS-concurrent-mark-start]
 ִcounter:6777
Heap
 par new generation   total 153600K, used 5705K [0x00000007a0c00000, 0x00000007ab2a0000, 0x00000007ab2a0000)
  eden space 136576K,   4% used [0x00000007a0c00000, 0x00000007a1192510, 0x00000007a9160000)
  from space 17024K,   0% used [0x00000007aa200000, 0x00000007aa200000, 0x00000007ab2a0000)
  to   space 17024K,   0% used [0x00000007a9160000, 0x00000007a9160000, 0x00000007aa200000)
 concurrent mark-sweep generation total 341376K, used 309716K [0x00000007ab2a0000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 5499K, capacity 5958K, committed 6016K, reserved 1056768K
  class space    used 595K, capacity 626K, committed 640K, reserved 1048576K
1 本次为YoungGC, Young区总大小为78656K, 整个堆总大小为253440K, GC后Young区使用量从69952K降到了8698K, 整个堆使用量从69952K降到了22083K.
2 CMS开始MinorGC, 此时Old区占用98614K/174784K, 整个堆占用108733K/253440K, 本次为开始标记阶段, 会暂停所有用户线程.
3 并发标记阶段, 与用户线程并发执行.
4 预清理阶段, 与用户线程并发执行.
5 可中断预清理阶段, 等待上一次YoungGC结束一段时间后再准备开始下一个阶段(为了防止YoungGC和最终标记连续暂停用户线程两次).
6 最终标记阶段, 此时暂停用户线程0.01秒.
7 并发清除阶段, 开始回收老年代, 此时GC线程与用户线程并发执行.
8 并发重置阶段.

12.5. G1

JDK9默认收集器

使用 -XX:+UseG1GC 启动G1收集器.

12.5.1. Region

G1将堆划分为多个大小相等的独立区域, 每个区域可以根据需要扮演Eden/Survivor/Old/Humongous空间.
每个region大小限制在1MB~32MB之间, 通过 -XX:G1HeapRegionSize 参数指定.
如果不指定, 则默认为 min(32MB, max(round(Xmx/2048), 1MB)) , 如果计算出来超过32MB.

12.5.2. CardTable

CardTable是用来记录跨代引用关系的数据结构( point-out : 描述我指向谁). JVM全局维护一个bitmap数组, 大小为Xmx/512B, 每一位标识地址在 n*512(n+1)*512-1 区域的内存页.
如果该区域存在Old区引用Young区对象则为1, 否则为0.

12.5.3. RememberSet

每个region存在一个RememberSet, 存储有哪些Region引用当前Region的对象, 以及这些指针在CardTable的范围内.
如old区obj1.field = Young区的obj2, 则obj2所处的region的RememberSet会记录obj1所处region的地址, 以及obj1在region中的card索引号(从0开始).

12.5.4. 三色标记

在标记过程中从GC Root对象开始遍历所有引用对象, 使用三种颜色标识对象是否需要回收:

  • 黑色: 表示对象及其所有引用已被遍历过, 且该对象存活. 如果有其他对象引用该黑色对象, 则无需再次遍历.

  • 灰色: 表示对象被遍历到, 但其还有引用对象没被遍历到.

  • 白色: 表示对象没被遍历到, 即对象不可达需要被回收.

12.5.5. 并发标记

原始快照(SATB): 为解决并发标记过程中Mutator线程修改了遍历中或者遍历过的对象引用, 在并发标记过程中, 将灰色对象被Mutator线程删除白色对象引用时记录下该灰色对象, 待并发标记阶段结束后,
再重新遍历一次灰色对象及其 原始的 引用对象集合.

12.5.6. G1主要回收流程

YoungGC
  1. 遍历RootSet和Young区的RememberSet对象.

  2. 将根对象和RememberSet中的对象和他们引用的对象复制到Survivor区.

  3. 清理Eden和CardTable, 重新构建Young区的RememberSet.

MixGC
  1. 初始标记(STW): 标记 GC Roots 对象.

  2. 并发标记: 从 GC Roots 对象开始遍历整个对象图(三色标记: 黑色为存活对象, 灰色为遍历中对象, 白色为待回收对象).

  3. 最终标记(STW): 修正并发标记期间, 因用户程序继续运作而导致标记变动的那部分对象的标记记录.

  4. 筛选回收(STW): 计算每个待回收old region的代价找到回收收益最高的若干region, 和所有的young region一起回收, 将存活下来的对象复制到空的Region中再清空原来的Region.

12.6. ZGC

JDK11新增的收集器

使用 -XX:+UseZGC 启动ZGC收集器.

ZGC回收流程
  1. 并发标记

  2. 并发预备重分配

  3. 并发重分配

  4. 并发重映射

13. GC调优

13.1. 选择恰当的垃圾收集器

  • 数据分析/科学计算类应用, 关注吞吐量. ParallelGC

  • 事务型应用, 关注低延迟. CMS/G1/ZGC

  • 客户端/嵌入式设备应用, 关注内存占用. Serial

13.2. 永远不要把堆内存的大小设置的比机器物理内存大

推荐 -Xms=-Xmx=80%机器内存, -XX:NewRatio=1.

Full GC时JVM会访问整个堆的内容, 内存交换会拖慢GC的速度. 使用-Xms和-Xmx分别设置堆内存的最小值和最大值.
默认64位JVM堆内存初始值为取512MB和物理内存大小1/64二者中的最小值, 最大值取32G和物理内存大小1/4二者中的最小值.

13.3. 调整新生代与老年代的大小

  1. G1GC不设置Xmn, 让JVM自动调整新生代和老年代的大小

  2. 新生代大小设置参数, 后者优先级更高

    • -XX:NewRatio 设置新生代与老年代的比例, 默认值为2. 默认情况下新生代大小初始值等于 初始堆大小/(1+NewRatio)

    • -XX:NewSize 设置新生代大小的初始值

    • -XX:MaxNewSize 设置新生代大小的最大值

    • -Xmn 同时设置新生代的初始值和最大值

13.4. 记录GC日志以便分析

示例: -Xlog:gc*:file=logs/gc-%t.log:time,tags:filecount=20,filesize=20M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs

14. 调试JDK源码

  1. 下载openjdk源码: git clone https://github.com/openjdk/jdk.git

  2. 设置机器上当前使用的jdk(Boot JDK)版本为最新的发布版本.

  3. 编译jdk:

    1. bash configure --with-debug-level=slowdebug

    2. make all

    3. 等待编译结束, build目录下会生成jdk目录.

    4. make compile-commands

  4. 导入项目到CLION:

    1. 点击open, 选择编译好的jdk目录下的 compile_commands.json 文件.

    2. Tools → Compilation Database → Change Project Root 选择源码目录.

    3. Custom Build Targets 新增配置make和clean命令组合:

      1. Program: make , Arguments: CONF=编译好的jdk目录名 , Working directory: 源码目录 .

      2. Program: make , Arguments: CONF=编译好的jdk目录名 clean , Working directory: 源码目录 .

    4. Run/Debug Configurations 新增 Custom Build Application , Target选择上一步创建的Build命令组合, Executable 选择编译好的 java 可执行文件, Program Arguments 填想要运行的Java类全限定名, Working directory 填想要运行的Java类所在目录.

  5. 设置debug环境: echo "breakpoint set --file /path/to/jdk/src/java.base/share/native/launcher/main.c --line 98 -C "pro hand -p true -s false SIGSEGV SIGBUS" --auto-continue true" | tee ~./.lldbinit