GC

触发GC的条件

触发MinorGC 的条件:
Eden区满了

触发Full GC的条件:

Full GC触发条件:

(1)调用System.gc时,JVM并不会马上执行,执行时机不确定的

(2)老年代空间不足

(3)方法去空间不足

(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小,Eden or Survivor ----> To Survivor  如果不够则通过内存担保措施直接晋升到老年代。

第一个例子

命令行参数

-Xms20m
-Xmx20m
-Xmn10m
-verbose:gc
-XX:+PrintGCDetails
-XX:SurvivorRatio=8

先介绍一下每个参数的含义

  • Xms初始化堆内存、
  • Xmx 最大堆内存、
  • Xmn 是新生代的大小、
  • verbose:gc GC 日志中输出详细的GC信息 、
  • -XX:+PrintGcDetails打印 GC 细节与发生时间、
  • -XX:SurvivorRatio=8 Eden 区域跟Survivor区域的大小比值 Eden:Survivor 8:1 = 8 Eden:From Survivor: To Survivor

测试代码

public class MyGcTest02 {
    public static void main(String[] args) {
        int size  = 1024 * 1024; //1m 大小
        byte[] byteArray1 =new byte[2*size];
        byte[] byteArray2 =new byte[2*size];
        byte[] byteArray3 =new byte[2*size];
        byte[] byteArray4 =new byte[2*size];

        System.out.println("hello world");
    }
}

打印日志如下:

[GC (Allocation Failure) [PSYoungGen: 8141K->776K(9216K)] 8141K->6928K(19456K), 0.0038074 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 776K->0K(9216K)] [ParOldGen: 6152K->6768K(10240K)] 6928K->6768K(19456K), [Metaspace: 3130K->3130K(1056768K)], 0.0053604 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
hello world
Heap
 PSYoungGen      total 9216K, used 2371K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 28% used [0x00000000ff600000,0x00000000ff850c58,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 6768K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 66% used [0x00000000fec00000,0x00000000ff29c398,0x00000000ff600000)
 Metaspace       used 3170K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 344K, capacity 388K, committed 512K, reserved 1048576K

上面是GC的打印日志 分为两部分来分析他们:

GC 日志

一条Minor GC

[GC (Allocation Failure) [PSYoungGen: 8141K->776K(9216K)] 8141K->6928K(19456K), 0.0038074 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

一条Full GC

[Full GC (Ergonomics) [PSYoungGen: 776K->0K(9216K)] [ParOldGen: 6152K->6768K(10240K)] 6928K->6768K(19456K), [Metaspace: 3130K->3130K(1056768K)], 0.0053604 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
  1. 开头的部分有两种: GC 表示是Minor GC 、另外一种是Full GC. GC (Allocation Failure) 是内存分配失败的导致一次Minor GC,Eden 区满了。然后Full GC 是由于上面的第五条。

  2. GC 收集的内存变化

[PSYoungGen: 8141K->808K(9216K)] 

PSYoungGen : PS 表示Parallel Scavenge收集器 复制算法的收集器 内存大小的变化 7340K 内存发生了变化 其中部分被垃圾回收器清除了,部分复制到了To Survivor 区域

  1. 0.0045558 secs 垃圾回收时所用的事件
新生代
PSYoungGen      total 9216K, used 7364K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)

9216K / 1024 = 9m 我们设置了新生代的内存大小为10M ,Eden 跟 Survivor 空间的比值 8:1 但是有两个Surivor 所有只有一个Servivor 是可以用的。 所以就是9m

eden space 8192K 
from space 1024K
to   space 1024K 

eden 8m from 1m to 1m 但是to的used为0%。

当新生代已经无法容纳下一个新创建的对象,新创建的对象将直接在老年代创建
public class MyGcTest02 {
    public static void main(String[] args) {
        int size  = 1024 * 1024; //1m 大小
        byte[] byteArray1 =new byte[2*size];
        byte[] byteArray2 =new byte[2*size];
        byte[] byteArray4 =new byte[3*size];
        byte[] byteArray7 =new byte[3*size];

        System.out.println("hello world");


    }
}

上面的代码在非Dubug模式下并不会造成full gc

[GC (Allocation Failure) [PSYoungGen: 6093K->776K(9216K)] 6093K->4880K(19456K), 0.0026164 secs] [Times: user=0.09 sys=0.02, real=0.00 secs] 
hello world
Heap
 PSYoungGen      total 9216K, used 7241K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 78% used [0x00000000ff600000,0x00000000ffc50598,0x00000000ffe00000)
  from space 1024K, 75% used [0x00000000ffe00000,0x00000000ffec2020,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 4104K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 40% used [0x00000000fec00000,0x00000000ff002020,0x00000000ff600000)
 Metaspace       used 3170K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 344K, capacity 388K, committed 512K, reserved 1048576K

这里的应该是前两个分配完了,第三个字节数据的时候申请内存触发了Minor GC,结束新生代大小592K,前面创建的4m > To Survivor. 则直接晋身到老年代了(4104k) 有空间存放第一个3M存放后小于一半,第四个字节数组也不会触发GC ,第四个的创建也是新生代。

System.gc();

System.gc();方法会让JVM执行一次垃圾回收,它会触发以西Full Gc

System.gc();

启动参数如下:

-verbose:gc
-XX:+PrintGCDetails

Gc 日志如下:

[GC (System.gc()) [PSYoungGen: 3340K->744K(38400K)] 3340K->752K(125952K), 0.0008654 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 744K->0K(38400K)] [ParOldGen: 8K->595K(87552K)] 752K->595K(125952K), [Metaspace: 3029K->3029K(1056768K)], 0.0046903 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 38400K, used 998K [0x00000000d5d00000, 0x00000000d8780000, 0x0000000100000000)
  eden space 33280K, 3% used [0x00000000d5d00000,0x00000000d5df9b20,0x00000000d7d80000)
  from space 5120K, 0% used [0x00000000d7d80000,0x00000000d7d80000,0x00000000d8280000)
  to   space 5120K, 0% used [0x00000000d8280000,0x00000000d8280000,0x00000000d8780000)
 ParOldGen       total 87552K, used 595K [0x0000000081600000, 0x0000000086b80000, 0x00000000d5d00000)
  object space 87552K, 0% used [0x0000000081600000,0x0000000081694f68,0x0000000086b80000)
 Metaspace       used 3043K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 330K, capacity 388K, committed 512K, reserved 1048576K
元空间导致的Full GC
public class MyTest4 {

    public static void main(String[] args) {
        for (;;){
            Enhancer enhancer = new Enhancer();

            enhancer.setSuperclass(MyTest4.class);
            enhancer.setUseCache(false);
            enhancer.setCallback((MethodInterceptor)(var1, var2, args1, proxy) ->
                    proxy.invokeSuper(var1,args1)
            );
            try {
//                Thread.sleep(50);
            }catch (Exception e){
                e.printStackTrace();
            }

            Object o = enhancer.create();
        }
    }
}

GC 参数: -XX:MetaspaceSize=20m 元空间大小为20m

-verbose:gc
-XX:+PrintGCDetails
-XX:MetaspaceSize=20m 

Gc log

[GC (Allocation Failure) [PSYoungGen: 33280K->2349K(38400K)] 33280K->2357K(125952K), 0.0030541 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 35629K->2280K(38400K)] 35637K->2296K(125952K), 0.0031842 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 35560K->2776K(38400K)] 35576K->2800K(125952K), 0.0029843 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 36056K->3288K(71680K)] 36080K->3320K(159232K), 0.0030766 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 69848K->4792K(71680K)] 69880K->4832K(159232K), 0.0051226 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 71352K->5112K(137216K)] 71392K->5808K(224768K), 0.0061339 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 137208K->5504K(138240K)] 137904K->7800K(225792K), 0.0059711 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Metadata GC Threshold) [PSYoungGen: 128788K->5936K(269824K)] 131084K->9248K(357376K), 0.0044505 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Metadata GC Threshold) [PSYoungGen: 5936K->0K(269824K)] [ParOldGen: 3312K->8917K(62464K)] 9248K->8917K(332288K), [Metaspace: 20223K->20223K(1069056K)], 0.0474020 secs] [Times: user=0.27 sys=0.00, real=0.05 secs] 
[GC (Allocation Failure) [PSYoungGen: 263168K->2752K(270848K)] 272085K->11669K(333312K), 0.0026946 secs] [Times: user=0.09 sys=0.03, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 265920K->4916K(325632K)] 274837K->13833K(388096K), 0.0034723 secs] [Times: user=0.13 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 325428K->7136K(329728K)] 334345K->16085K(392192K), 0.0054073 secs] [Times: user=0.09 sys=0.03, real=0.00 secs] 
[GC (Metadata GC Threshold) [PSYoungGen: 58573K->7488K(396288K)] 67522K->16437K(458752K), 0.0060224 secs] [Times: user=0.08 sys=0.03, real=0.01 secs] 
[Full GC (Metadata GC Threshold) [PSYoungGen: 7488K->0K(396288K)] [ParOldGen: 8949K->16259K(91136K)] 16437K->16259K(487424K), [Metaspace: 33916K->33916K(1081344K)], 0.0341647 secs] [Times: user=0.16 sys=0.00, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 388096K->2592K(397312K)] 404355K->18851K(488448K), 0.0027530 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 390688K->4672K(504832K)] 406947K->20931K(595968K), 0.0035094 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

JDK1.8 默认的垃圾回收算法

使用 -XX:+PrintCommandLineFlags 会打印JVM 的命令行启动参数

-XX:InitialHeapSize=132730432 -XX:MaxHeapSize=2123686912 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 

C:\Users\86188>java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)

在JDK1.8.0.212 使用的是ParallelGC 、新生代是Parallel Scavenge 老年代是Parallel Old

JVM设置阈值 PretenureSizeThreshold

注意:
只有当使用的垃圾回收器是SerialGC的时候本参数才有效。下面我们设置PretenureSizeThreshold=4194304 (1024* 1024*4 = 4m) 当对象大于4M直接分配到老年代。

-verbose:gc
-Xmx20m
-Xms20m
-Xmn10m
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=4194304
-XX:+UseSerialGC

java代码如下我们直接生成一个5M的对象:

public class MyGcTest03 {
    public static void main(String[] args) {
        int size  = 1024 * 1024; //1m 大小
        byte[] byteArray1 =new byte[5*size];
    }
}

GC 的日志如下:

Heap
 def new generation   total 9216K, used 2161K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  26% used [0x00000000fec00000, 0x00000000fee1c590, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 5120K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  50% used [0x00000000ff600000, 0x00000000ffb00010, 0x00000000ffb00200, 0x0000000100000000)
 Metaspace       used 3113K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 338K, capacity 388K, committed 512K, reserved 1048576K

tenured generation 5120 就是5m 的大小.对象直接创建在了老年代。如果我们直接去掉-XX:UseSerialGC参数。但是保留PretenureSizeThreshold 对象还是创建在了Eden区。

Heap
 PSYoungGen      total 9216K, used 7281K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 88% used [0x00000000ff600000,0x00000000ffd1c5a0,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
 Metaspace       used 3131K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 342K, capacity 388K, committed 512K, reserved 1048576K

如果无法在新生代分配(对象的大小大于或者等于堆大小),直接在老年代创建,且不会触发GC

public class MyGcTest05 {
    public static void main(String[] args) {
        int size  = 1024 * 1024; //1m 大小
        byte[] byteArray1 =new byte[8*size]; // 不会触发Minor GC 
    }
}

GC日志如下,对象直接创建在了老年代:

Heap
 PSYoungGen      total 9216K, used 2320K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 28% used [0x00000000ff600000,0x00000000ff844260,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 10240K, used 8192K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 80% used [0x00000000fec00000,0x00000000ff400010,0x00000000ff600000)
 Metaspace       used 3042K, capacity 4556K, committed 4864K, reserved 1056768K
  class space    used 323K, capacity 392K, committed 512K, reserved 1048576K

下面的情况则会触发一次Minor GC

    byte[] byteArray2 =new byte[4*size];
    byte[] byteArray1 =new byte[7*size];
如果老年代满了就会触发Full GC

我们创建两个8m 的Eden区的大小还是8m 因为无法分配导致直接两个都会分配到老年代,老年代是10m,无法容纳第二个对象就会触发Full GC ,并且会出现OOM。

    byte[] byteArray2 =new byte[8 *size];
    byte[] byteArray3 =new byte[8 *size];

无法分配导致的Full GC,完了还是无法分配就会抛出OutOfMemory:

[GC (Allocation Failure) [PSYoungGen: 1997K->776K(9216K)] 10189K->8976K(19456K), 0.0008958 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 776K->696K(9216K)] 8976K->8896K(19456K), 0.0008225 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 696K->0K(9216K)] [ParOldGen: 8200K->8798K(10240K)] 8896K->8798K(19456K), [Metaspace: 3167K->3167K(1056768K)], 0.0046045 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] 8798K->8798K(19456K), 0.0002650 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(9216K)] [ParOldGen: 8798K->8780K(10240K)] 8798K->8780K(19456K), [Metaspace: 3167K->3167K(1056768K)], 0.0051171 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at com.sqhtech.gc.MyGcTest06.main(MyGcTest06.java:7)
Heap
 PSYoungGen      total 9216K, used 410K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 5% used [0x00000000ff600000,0x00000000ff666800,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 8780K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 85% used [0x00000000fec00000,0x00000000ff493140,0x00000000ff600000)
 Metaspace       used 3232K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 351K, capacity 388K, committed 512K, reserved 1048576K

动态的年龄调整以及 MaxTenuringThreshold 最大阈值的设置

启动参数

/**

  • 经历了多次GC后存活的对象可以在From Survivor To Servivor之间来回复制
  • 每一复制就是1岁
  • -XX:MaxTenuringThreshold=4 设置晋升到老年代对象的最大的存活年龄。
  • 这个阈值是可以动态调整的,设置值为最大值
  • JVM可以在年龄等于2的时候就吧新生代的对象晋升到老年代,但是最大值不会超过
  • 设置的值
  • ParallGC 15 CMS 默认为6 G1 默认为15
  • 如果发现到了某个年龄后总大小以及大于Survivor空间的50%那么这时候就需要自动的调整
  • 阈值,不能再继续等到默认的15次GC后才能完成晋升。会导致空间大小不足,导致对象直接从
  • Eden区晋升到老年代
  • -XX:+PrintTenuringDistribution 打印GC日志
    */
-verbose:gc
-Xmx20m
-Xms20m
-Xmn10m
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=5
-XX:+PrintTenuringDistribution

java 不停的创建对象,触发GC

    public static void main(String[] args) {
        int  size = 1024 * 1024 ;
        for(int i= 0;i<1000;i++){
           try {
               Thread.sleep(50);
               byte[] buffer = new byte[ 1 *size];
           }catch (Exception e){
               e.printStackTrace();
           }

        }
    }

GC log

Desired survivor size 1048576 bytes, new threshold 5 (max 5)
[PSYoungGen: 8097K->776K(9216K)] 8105K->784K(19456K), 0.0008843 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) 
Desired survivor size 1048576 bytes, new threshold 4 (max 5)
[PSYoungGen: 8099K->824K(9216K)] 8107K->832K(19456K), 0.0008337 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) 
Desired survivor size 1048576 bytes, new threshold 3 (max 5)
[PSYoungGen: 8149K->0K(9216K)] 8157K->688K(19456K), 0.0009346 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) 
Desired survivor size 1048576 bytes, new threshold 2 (max 5)
[PSYoungGen: 7326K->0K(9216K)] 8014K->688K(19456K), 0.0003190 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) 
Desired survivor size 1048576 bytes, new threshold 1 (max 5)
[PSYoungGen: 7327K->0K(9216K)] 8015K->688K(19456K), 0.0004307 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

Desired survivor size 1048576 bytes, new threshold 1 (max 5)

new threshold num 这里的数值就是实际晋升到老年代的年龄的大小 ,这里的max如果内存分配不频繁的时候GC 还将可能变为最大值。

美团技术博客 : 从实际案例聊聊Java应用的GC优化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值