Java对象的GC内存分配和回收策略以及GC日志的查看教程

673 阅读29分钟

「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战」。

详细介绍了Java对象的GC内存分配和回收策略以及常见GC日志参数。

1 GC日志的查看

关于垃圾收集器,我们已经了解了很多,如果垃圾回收频繁出现,或者占用了太长的CPU时间, 就不得不引起重视。此时, 就需要一些跟踪参数来进一步甄别垃圾回收器的效率和效果,我们需要学会输出和产看GC日志,本文基于JDK1.8+IDEA,帮助大家输出和查看GC日志。

1.1 GC日志常用相关参数

参数 功能
-XX:+PrintGC 输出简单GC日志
-XX:+PrintGCDetails 输出详细GC日志,并且虚拟机退出前打印堆栈使用信息。
-XX:+PrintHeapAtGC 在每次GC前后都打印堆栈信息
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式),JVM启动后的时间偏移量。
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如2020-04-02T10:09:01.045+0800)
-Xloggc:./gc.log 日志文件的输出路径
-XX:SurvivorRatio=8 定义了年轻代中Eden区与一个Survivor区的空间比例是8:1
-Xmx 设定程序运行期间最大可占用的内存大小
-Xms 设定程序启动时占用内存大小,大一点一般启动更快
-Xmn 设置年轻代占用内存大小
-XX:MaxTenuringThreshold=N 对象年龄达到N后,下一次GC将进入老年代,可以为0

1.2 GC日志的输出和查看

该部分内容将在Java对象的GC内存分配和回收策略章节中的案例中为大家介绍。

2 Java对象的GC内存分配和回收策略

2.1 对象优先在Eden分配

大多数情况下,对象在新生代Eden区中分配。

2.1.1 测试

public class GCLog {

    public static final int _1MB = 1024 * 1024;
    public static final int _1KB = 1024 ;

    public static void main(String[] args) {
        eden();
    }

    /**
     * vm参数:  -Xmx64M -Xms64M -Xmn10M -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCDetails
     * 对象首先在Eden区分配
     */
    public static void eden() {
        byte[] b1, b2, b3, b4, b5;
        b1 = new byte[1 * _1MB];
        b2 = new byte[1 * _1MB];
        b3 = new byte[1 * _1MB];
        b4 = new byte[1 * _1MB];
        b5 = new byte[1 * _1MB];
    }
}

2.1.1.1 idea配置vm参数

点击下拉箭头,点击Edit Configurations

在这里插入图片描述

点击+,点击Application

在这里插入图片描述

点击选择对应的Class,name 自己随意想改就改

在这里插入图片描述

配置VM参数,

-Xmx64M -Xms64M -Xmn10M -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCDetails

点击Apply,完成。

在这里插入图片描述

2.1.2 输出GC日志解释

运行,查看输出:

Heap
 PSYoungGen      total 9216K, used 7516K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 91% used [0x00000000ff600000,0x00000000ffd57060,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 55296K, used 0K [0x00000000fc000000, 0x00000000ff600000, 0x00000000ff600000)
  object space 55296K, 0% used [0x00000000fc000000,0x00000000fc000000,0x00000000ff600000)
 Metaspace       used 3412K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 374K, capacity 388K, committed 512K, reserved 1048576K

解读:

PSYoungGen中最前面的PS代表垃圾收集器是Parallel Scavenge收集器,回收的区域是新生代(YoungGen);

total 9216K, used 7516K中total表示新生代总共可使用内存,userd表示使用内存。

下面是新生代的三个分区:eden space、from space、to space 他们的大小默认是8:1:1。

ParOldGen中最前面的Par代表垃圾收集器是Parallel Old收集器,回收的区域是老年代(OldGen),包含object space(对象区域),占比100%。

Metaspace即JDK1.8的元空间,包含class space(类数据区域)。

eden space使用了91% used,而from space、to space,以及object space都使用了0%,说明案例中的对象是分配在eden区的。

2.2 老年对象将进入老年代

虚拟机给每个对象定义了一个对象年龄计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能够被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设置为1。对象在Survivor区每熬过一次Minor GC,年龄就增加一岁,当它的年龄增加到一定程度(默认15岁)时,下一次GC就会被晋升到老年代中。对象晋升老年代的年龄最大值,可以通过参数 -XX:MaxTenuringThreshold=N来设置。

2.2.1 测试

public class GCLog {

    private static final int _1MB = 1024 * 1024;
    public static final int _1KB = 1024;

    public static void main(String[] args) {
        toOld();
    }

    /**
     * vm参数: 
     * 对象首先在Eden区分配,年老对象进入老年代
     */
    private static void toOld() {
        for (int i = 0; i < 15; i++) {
            byte[] b2 = new byte[_1MB];
        }
    }
}

2.2.2 输出GC日志解释

首先输入VM参数:

-Xmx20M -Xms20M -Xmn10M -XX:+PrintGCTimeStamps -XX:SurvivorRatio=4 -XX:+PrintGC -XX:MaxTenuringThreshold=1 -XX:+PrintHeapAtGC

表示在第二次GC时,1岁的对象将直接进入老年代。

输出如下:

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 8704K, used 6159K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 85% used [0x00000000ff600000,0x00000000ffc03f88,0x00000000ffd00000)
  from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
 ParOldGen       total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
 Metaspace       used 3465K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 380K, capacity 388K, committed 512K, reserved 1048576K
0.101: [GC (Allocation Failure) [PSYoungGen: 6159K->888K(8704K)] 6159K->896K(18944K), 0.0010425 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 8704K, used 888K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffd00000)
  from space 1536K, 57% used [0x00000000ffd00000,0x00000000ffdde010,0x00000000ffe80000)
  to   space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
 ParOldGen       total 10240K, used 8K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec02000,0x00000000ff600000)
 Metaspace       used 3465K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 380K, capacity 388K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=2 (full 0):
 PSYoungGen      total 8704K, used 7168K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 87% used [0x00000000ff600000,0x00000000ffc22238,0x00000000ffd00000)
  from space 1536K, 57% used [0x00000000ffd00000,0x00000000ffdde010,0x00000000ffe80000)
  to   space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
 ParOldGen       total 10240K, used 8K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec02000,0x00000000ff600000)
 Metaspace       used 3465K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 380K, capacity 388K, committed 512K, reserved 1048576K
0.103: [GC (Allocation Failure) [PSYoungGen: 7168K->0K(8704K)] 7176K->732K(18944K), 0.0008609 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap after GC invocations=2 (full 0):
 PSYoungGen      total 8704K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffd00000)
  from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
 ParOldGen       total 10240K, used 732K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 7% used [0x00000000fec00000,0x00000000fecb7090,0x00000000ff600000)
 Metaspace       used 3465K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 380K, capacity 388K, committed 512K, reserved 1048576K
}
Heap
 PSYoungGen      total 8704K, used 5322K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 74% used [0x00000000ff600000,0x00000000ffb32b38,0x00000000ffd00000)
  from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
 ParOldGen       total 10240K, used 732K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 7% used [0x00000000fec00000,0x00000000fecb7090,0x00000000ff600000)
 Metaspace       used 3471K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 381K, capacity 388K, committed 512K, reserved 1048576K

上面的日志就是在每次GC前后打印堆内存信息。在最后再次打印堆内存信息。

GC信息解释:

  1. 0.101: [GC (Allocation Failure) [PSYoungGen: 6159K->888K(8704K)] 6159K->896K(18944K), 0.0010425 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
  2. 0.101:相对JVM启动时的时间
  3. GC:表明进行了一次垃圾回收,前面没有Full修饰,表明这是一次Minor GC。
  4. Allocation Failure:表明本次引起GC的原因是因为在年轻代中没有足够的空间能够存储新的数据了。
  5. PSYoungGen:表明本次GC发生在年轻代并且使用的是Parallel Scavenge收集器垃圾收集器。
  6. 6159K->888K(8704K):三个参数分别为:GC前该内存区域(这里是年轻代)使用容量,GC后该内存区域使用容量,该内存区域总容量。
  7. 6159K->896K(18944K):三个参数分别为:堆区垃圾回收前的使用大小,堆区垃圾回收后的使用大小,堆区总大小。
  8. 0.0010425 secs:该内存区域GC耗时,单位是秒
  9. [Times: user=0.00 sys=0.00, real=0.00 secs]:分别表示用户态耗时,内核态耗时和总耗时

看看其他信息:

Heap after GC invocations=1 (full 0)

表示第一次GC之后的信息,我们可以在下面看到from space 1536K, 57% used,表明第一次GC之后survivor区占用了大概一半多

Heap after GC invocations=2 (full 0)

表示第二次GC之后的信息,我们可以在下面看到ParOldGen total 10240K, used 732K,即有存活的对象转移到了老年代。

将-XX:MaxTenuringThreshold=1 的值改成2,输出如下

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 8704K, used 6159K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 85% used [0x00000000ff600000,0x00000000ffc03f88,0x00000000ffd00000)
  from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
 ParOldGen       total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
 Metaspace       used 3436K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K
0.098: [GC (Allocation Failure) [PSYoungGen: 6159K->808K(8704K)] 6159K->816K(18944K), 0.0008813 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 8704K, used 808K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffd00000)
  from space 1536K, 52% used [0x00000000ffd00000,0x00000000ffdca020,0x00000000ffe80000)
  to   space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
 ParOldGen       total 10240K, used 8K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec02000,0x00000000ff600000)
 Metaspace       used 3436K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 376K, capacity 388K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=2 (full 0):
 PSYoungGen      total 8704K, used 7162K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 88% used [0x00000000ff600000,0x00000000ffc347e8,0x00000000ffd00000)
  from space 1536K, 52% used [0x00000000ffd00000,0x00000000ffdca020,0x00000000ffe80000)
  to   space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
 ParOldGen       total 10240K, used 8K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec02000,0x00000000ff600000)
 Metaspace       used 3445K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 377K, capacity 388K, committed 512K, reserved 1048576K
0.100: [GC (Allocation Failure) [PSYoungGen: 7162K->792K(8704K)] 7170K->808K(18944K), 0.0011583 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap after GC invocations=2 (full 0):
 PSYoungGen      total 8704K, used 792K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffd00000)
  from space 1536K, 51% used [0x00000000ffe80000,0x00000000fff46030,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
 ParOldGen       total 10240K, used 16K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec04000,0x00000000ff600000)
 Metaspace       used 3445K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 377K, capacity 388K, committed 512K, reserved 1048576K
}
Heap
 PSYoungGen      total 8704K, used 6306K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7168K, 76% used [0x00000000ff600000,0x00000000ffb62900,0x00000000ffd00000)
  from space 1536K, 51% used [0x00000000ffe80000,0x00000000fff46030,0x0000000100000000)
  to   space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
 ParOldGen       total 10240K, used 16K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec04000,0x00000000ff600000)
 Metaspace       used 3458K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 379K, capacity 388K, committed 512K, reserved 1048576K

我们可以看到第2次GC之后object space 10240K, 0% used,说明1岁的对象并没有转移到老年代。

2.2.3 动态判定对象年龄

为了能更好地适应不同程序的内存状况,虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

2.3 大对象直接进入老年代

所谓的大对象是指,1、所需内存大于剩余内存的对象,2、所需连续内存空间大于剩余连续内存空间的对象,最典型的大对象就是那种很长的字符串以及数组(比如byte[]数组)。

由于eden和survivor区都无法容纳大对象,那么就会被尝试直接晋升到老年代。

虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。此参数只对串行回收器以及ParNew回收有效, 而对ParallelGC回收器无效,默认值为0。

2.3.1 案例

public class GCLog {
    private static final int _1MB = 1024 * 1024;
    public static final int _1KB = 1024;

    public static void main(String[] args) {
        toOld();
    }

    /**
     * vm参数: -Xmx7M -Xms7M -Xmn2M -XX:+PrintGCDetails
     * 大对象直接进入老年代
     */
    private static void toOld() {
        //byte[] bigB1 = new byte[2 * _1MB];
        //byte[] bigB2 = new byte[3 * _1MB];
    }
}

设置VM参数:

-Xmx7M -Xms7M -Xmn2M -XX:+PrintGCDetails

没有设置对象年龄,那么默认是15。运行三次,分别是不开放两个注释,开放第一个注释,开放第二个注释。结果如下:

第一次

[GC (Allocation Failure) [PSYoungGen: 1023K->504K(1536K)] 1023K->608K(7680K), 0.0009661 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 1536K, used 1324K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
  eden space 1024K, 80% used [0x00000000ffe00000,0x00000000ffecd210,0x00000000fff00000)
  from space 512K, 98% used [0x00000000fff00000,0x00000000fff7e010,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 6144K, used 104K [0x00000000ff800000, 0x00000000ffe00000, 0x00000000ffe00000)
  object space 6144K, 1% used [0x00000000ff800000,0x00000000ff81a040,0x00000000ffe00000)
 Metaspace       used 3431K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 375K, capacity 388K, committed 512K, reserved 1048576K

第二次

[GC (Allocation Failure) [PSYoungGen: 1023K->504K(1536K)] 1023K->640K(7680K), 0.0011018 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 1536K, used 1332K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
  eden space 1024K, 80% used [0x00000000ffe00000,0x00000000ffecf2a8,0x00000000fff00000)
  from space 512K, 98% used [0x00000000fff00000,0x00000000fff7e010,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 6144K, used 2184K [0x00000000ff800000, 0x00000000ffe00000, 0x00000000ffe00000)
  object space 6144K, 35% used [0x00000000ff800000,0x00000000ffa22050,0x00000000ffe00000)
 Metaspace       used 3443K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 377K, capacity 388K, committed 512K, reserved 1048576K

第三次

[GC (Allocation Failure) [PSYoungGen: 1023K->504K(1536K)] 1023K->616K(7680K), 0.0009587 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 1536K, used 1375K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
  eden space 1024K, 85% used [0x00000000ffe00000,0x00000000ffed9d70,0x00000000fff00000)
  from space 512K, 98% used [0x00000000fff00000,0x00000000fff7e010,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 6144K, used 3184K [0x00000000ff800000, 0x00000000ffe00000, 0x00000000ffe00000)
  object space 6144K, 51% used [0x00000000ff800000,0x00000000ffb1c050,0x00000000ffe00000)
 Metaspace       used 3471K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 381K, capacity 388K, committed 512K, reserved 1048576K

从结果可以看到,由于2MB和3MB的对象大于新生代空间,因此,直接分配到了老年代,造成老年代内存被占用。实际上,如果老年代也无法分配,那么就会进行Full GC,最后抛出OOM异常。

2.4 TLAB上分配

对象主要分配在新生代的Eden区上,但是如果启动了本地线程分配缓冲,将按线程优先在专属TLAB(Thread Local Allocation Buffer,是线程私有的一块内存,如果设置了-XX:+UseTLAB,在线程初始化时,会申请一块指定大小的内存,只供当前线程使用,每个线程都有一个单独的Buffer,如果要分配内存,就在自己的Buffer上分配,不存在竞争的情况,能够提高分配的效率,但是当Buffer容量不够的时候,再重新从Eden区域申请一块继续使用)上分配。TLAB的空间是从Eden中划分出来的。

JDK1.8默认开启TLAB分配,可使用cmd命令 java -XX:+PrintFlagsFinal查看。 在这里插入图片描述

2.5 内存空间分配担保

在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

前面提到过,新生代使用复制收集算法,但为了内存利用率,只使用了其中一个Survivor空间来作为轮换备份,因此当出现大量对象在Minor GC后仍然存活的情况下,就需要老年代进行分配担保,让Survivor无法容纳的对象直接进入老年代。但是前提老年代本身还有足够空间容纳这些对象。但是实际完成内存回收前是无法知道多少对象存活,所以只好取之前每一次回收晋升到老年代对象容量的平均值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC来让老年代腾出更多的空间。

取平均值进行比较其实仍然是一种动态概率手段,也就是说如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败(HandlePronotion Failuer)。如果出现担保失败,那就只好在失败后重新发起一次Full GC。

JDK 6 Update 24之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略。

2.6 System.gc()

System.gc()会同时触发老年代和新生代进行回收。默认情况下, System.gc()即时生效,它会使用传统的Full GC的方式回收整个堆,即Serial+ Serial Old,并且会忽略参数中的UseG1GC和UseConcMarkSweepGC。

使用-XX:+ExplicitGCInvokesConcurrent后,System.gc()这种显示GC会使用并发发的方式进行回收。

而一般情况是我们认为,垃圾回收时自动进行的,无需手动触发。频繁的垃圾回收对系统性能造成较大影响。可以使用-XX:+DisableExplicitGC,则禁用显示GC,使得System.gc()等价于一个空函数。

对于默认并发回收器的JVM,例如JDK1.8使用System.gc()会在Full GC前触发一次Yong GC。这样做的目的是先将新生代进行一次收集,避免将所有回收工作同时交给一次Full GC进行,这样可以缩短一次停顿时间。-XX:-ScavengeBeforeFullGC 可以关闭这个特性,默认的情况下为true。

案例,vm 参数为 -XX:+PrintGC

public class GCLog {
    public static void main(String[] args) {
        sysGc();
    }

    private static void sysGc() {
        System.gc();
    }
}

输出结果可以看到JDK1.8调用gc方法,进行了两次GC。

[GC (System.gc())  5207K->848K(249344K), 0.0011507 secs]
[Full GC (System.gc())  848K->643K(249344K), 0.0044896 secs]

相关文章:

  1. 《深入理解Java虚拟机》

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!