GC与GC日志说明


GC日志内容说明

GC与GC日志说明

相关代码

/**
 * Survivor区放不下存活对象,部分对象进入老年代
 * -XX:NewSize=10m -XX:MaxNewSize=10m -Xms20m -Xmx20m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC 
 * -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10m
 */
public class JvmDemo {
    public static void main(String[] args) throws InterruptedException {
        byte[] bytes1 = new byte[1024 * 1024];//1m
        bytes1 = new byte[1024 * 1024];//1m
        bytes1 = new byte[1024 * 1024];//1m
        //直接分配一个5m的对象,由于eden区只有8m,之前已经分配了3m再加上一些未知对象也会占据一定的内存空间,此时必然会引起新生代gc
        byte[] bytes3 = new byte[5 * 1024 * 1024];//5m
    }
}

GC日志

0.161: [GC (Allocation Failure) 0.161: [ParNew: 5753K->890K(9216K), 0.0014072 secs] 5753K->1916K(19456K), 0.0014761 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 9216K, used 6176K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  64% used [0x00000000fec00000, 0x00000000ff129a40, 0x00000000ff400000)
  from space 1024K,  86% used [0x00000000ff500000, 0x00000000ff5de970, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 concurrent mark-sweep generation total 10240K, used 1026K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 3530K, capacity 4504K, committed 4864K, reserved 1056768K
  class space    used 393K, capacity 396K, committed 512K, reserved 1048576K

日志解读

0.161: [GC (Allocation Failure) 0.161: [ParNew: 5753K->890K(9216K), 0.0014072 secs] 5753K->1916K(19456K), 0.0014761 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

GC

表明进行了一次垃圾回收,前面没有Full修饰,表明这是一次Minor GC ,注意它不表示只GC新生代,并且现有的不管是新生代还是老年代都会STW。

Allocation Failure

表明本次引起GC的原因是因为在年轻代中没有足够的空间能够存储新的数据了。

ParNew

表明本次GC发生在年轻代并且使用的是ParNew垃圾收集器。ParNew是一个Serial收集器的多线程版本,会使用多个CPU和线程完成垃圾收集工作(默认使用的线程数和CPU数相同,可以使用-XX:ParallelGCThreads参数限制)。该收集器采用复制算法回收内存,期间会停止其他工作线程,即Stop The World。

5753K->890K(9216K), 0.0014072 secs

单位是KB

前三个参数分别为:GC前该内存区域(这里是年轻代)使用容量,GC后该内存区域使用容量,该内存区域总容量。

0.0014072 secs 是该内存区域GC耗时,单位是秒

5753K->1916K(19456K), 0.0014761 secs

前三个参数分别为:堆区垃圾回收前的大小,堆区垃圾回收后的大小,堆区总大小。

[Times: user=0.00 sys=0.00, real=0.00 secs]:

分别表示用户态耗时,内核态耗时和总耗时

结论

分析下可以得出结论:

GC新生代减少了5753 - 890 = 4863K
Heap区总共减少了5753 - 1916 = 3837K

4863 – 3837 = 1026K,说明该次共有1026内存从年轻代移到了老年代。

我们限制新生代的大小为10M,方法中有两个强引用,总共占用6M的空间,而回收后ParNew占用6M多。5M的对象并没有分配到老年代中。

相关代码2

/**
 * Survivor区放不下存活对象,部分对象进入老年代
 * -XX:NewSize=10m -XX:MaxNewSize=10m -Xms20m -Xmx20m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
 * -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10m
 */
public class JvmDemo {
    public static void main(String[] args) throws InterruptedException {
        byte[] bytes1 = new byte[1024 * 1024];//1m
        bytes1 = new byte[1024 * 1024];//1m
        bytes1 = new byte[1024 * 1024];//1m
        byte[] bytes0 = new byte[128 * 1024];//128kb
        //直接分配一个5m的对象,由于eden区只有8m,之前已经分配了3m再加上一些未知对象也会占据一定的内存空间,此时必然会引起新生代gc
        byte[] bytes3 = new byte[5 * 1024 * 1024];//5m
    }
}

gc日志如下:

0.171: [GC (Allocation Failure) 0.171: [ParNew: 5881K->1022K(9216K), 0.0012652 secs] 5881K->2048K(19456K), 0.0013266 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 9216K, used 6308K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  64% used [0x00000000fec00000, 0x00000000ff129a10, 0x00000000ff400000)
  from space 1024K,  99% used [0x00000000ff500000, 0x00000000ff5ff8f0, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 concurrent mark-sweep generation total 10240K, used 1026K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 3530K, capacity 4504K, committed 4864K, reserved 1056768K
  class space    used 393K, capacity 396K, committed 512K, reserved 1048576K

与上例比较发现,我们只是新建了一个128K的对象。根据GC信息看,老年代依然占据着1026K的内存空间。但是from区的内存被占用比例从86%升到了99%,这对比下来就很直观了,128k的对象去了哪里?肯定是from区,因为老年代被占用的内存大小根本没变。

如果Survivor区放不下存活对象,存活对象并不是全都进入老年代,而是部分对象进入老年代。

相关代码3

/**
 * Survivor区放不下存活对象,部分对象进入老年代
 * -XX:NewSize=10m -XX:MaxNewSize=10m -Xms20m -Xmx20m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
 * -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=8m
 */
public class JvmDemo2 {
    public static void main(String[] args) throws InterruptedException {
        byte[] data0 = new byte[8 * 1024 * 1024];
        data0 = null;
        byte[] data = null;
        //请求4m
        for (int i = 0; i < 4; i++) {
            //每个请求10m
            data = new byte[1024 * 1024];
        }
        data = null;
        byte[] data1 = new byte[5 * 1024 * 1024];

        byte[] data2 = new byte[1024 * 1024];
        byte[] data3 = new byte[1024 * 1024];

        data3 = new byte[1024 * 1024];
        data3 = new byte[1024 * 1024];
    }
}

相关的GC日志为:

0.163: [GC (Allocation Failure) 0.163: [ParNew: 6777K->850K(9216K), 0.0007905 secs] 14969K->9042K(19456K), 0.0008434 secs] [Times: user=0.02 sys=0.01, real=0.00 secs] 
0.164: [GC (Allocation Failure) 0.164: [ParNew (promotion failed): 8266K->7790K(9216K), 0.0007650 secs]0.165: [CMS: 8218K->7922K(10240K), 0.0028139 secs] 16458K->7922K(19456K), [Metaspace: 3538K->3538K(1056768K)], 0.0036178 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.168: [GC (CMS Initial Mark) [1 CMS-initial-mark: 7922K(10240K)] 8946K(19456K), 0.0002572 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.168: [CMS-concurrent-mark-start]
Heap
 par new generation   total 9216K, used 2449K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  29% used [0x00000000fec00000, 0x00000000fee64460, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 concurrent mark-sweep generation total 10240K, used 7922K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 3545K, capacity 4536K, committed 4864K, reserved 1056768K
  class space    used 395K, capacity 428K, committed 512K, reserved 1048576K

promotion failed(晋升失败)

最后存活应该是7M

新生代gc后,如果触发了老年代gc,,即使survivor区放的下部分存活对象,对象也会全部进入老年代。

感觉理论支撑应该是:当 Survivor 空间中相同年龄所有对象的大小总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,而不需要达到默认的分代年龄。

引用

https://blog.csdn.net/weixin_38106322/article/details/109122814


文章作者: WangQingLei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WangQingLei !
 上一篇
接口API设计规范 接口API设计规范
在设计接口时,有很多因素要考虑,如接口的业务定位,接口的安全性,接口的可扩展性、接口的稳定性、接口的跨域性、接口的协议规则、接口的路径规则、接口单一原则、接口过滤和接口组合等诸多因素,本篇文章将简要分析这些因素。
2023-08-18
下一篇 
work-summary-remove work-summary-remove
机房迁移过程中,当服务从阿里云迁移到机房网络后,由于网络、网关、运行环境的差异,服务在机房部署后,可能会出现由于跨网访问数据库、其他服务引起的响应时间过大、访问不通问题。为了方便识别此类问题,提供相应的监控,协助迁移研发发现响应状态、响应时
2023-07-27
  目录