A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

JVM内存分配

GC日志指令参数
描述

-XX:+PrintGCDetails
输出GC的详细日志

XX:+PrintGCTimeStamps
输出GC的时间戳(以基准时间的形式)

-XX:+PrintGCDateStamps
输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)

-XX:+PrintHeapAtGC
在进行GC的前后打印出堆的信息

-XX:+PrintGCApplicationStoppedTime
输出GC造成应用暂停的时间

-Xloggc:../logs/gc.log
日志文件的输出路径

-XX:+UseSerialGC
开启Serial收集器

-XX:+UseParallelGC -XX:+UseParallelOldGC
开启Parallel收集器

-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
开启CMS收集器

-XX:+UseG1GC
开启G1收集器

-XX:MaxTenuringThreshold
垃圾的最大年龄,值是从1到15,默认值是15

-XX:+PrintTenuringDistribution
显示在survivor空间里面有效的对象的岁数情况

GC日志分析
file:///C:/Users/Administrator/Desktop/Notes/resources/C0D94DB175FEF5E2AC68670A707C5F99.jpg
public class Neicunfenpei {
    public static void main(String[] args) {

        byte[] b1 = new byte[4*1024*1024];
        byte[] b3 = new byte[4*1024*1024];
        byte[] b2 = new byte[14*1024*1024];

        System.gc();

    }
}


控制台输出的GC日志信息
file:///C:/Users/Administrator/Desktop/Notes/resources/544CB46478F8944FBDFA63538917DAE8.jpg
0.289: [GC (System.gc()) [PSYoungGen: 25865K->4656K(38400K)] 25865K->23096K(125952K), 0.0175657 secs] [Times: user=0.01 sys=0.01, real=0.02 secs]
0.306: [Full GC (System.gc()) [PSYoungGen: 4656K->0K(38400K)] [ParOldGen: 18440K->22977K(87552K)] 23096K->22977K(125952K), [Metaspace: 3080K->3080K(1056768K)], 0.0142619 secs] [Times: user=0.01 sys=0.01, real=0.02 secs]
输出数据
含义

0.289、0.306
GC发生的时间,java虚拟机启动以来经过的秒数

[GC
是垃圾回收的停顿类型,而不是区分是新生代还是年老代

[Full GC
是垃圾回收的停顿类型,而不是区分是新生代还是年老代,发生了Stop-The-World

[Full GC (System)
是垃圾回收的停顿类型,而不是区分是新生代还是年老代,发生了Stop-The-World,调用 System.gc() 触发的垃圾回收

[DefNew, [Tenured, [Perm
Serial 收集器中新生代名为 Default New Generation

[ParNew
ParNew收集器表示 Parallel New Generation ,新生代名称

PSYoungGen
Parallel Scavenge 收集器 ,新生代名称

25865K->4656K(38400K)
方括号内部显示的表示 GC 前该区域已使用容量 -> GC 后该区域已使用容量 (该区域内存总容量)

25865K->23096K(125952K)
GC 前Java堆已使用容量 -> GC后Java堆已使用容量 (Java堆总容量)

0.0175657 secs
该区域GC所占用的时间,单位是秒

[Times: user=0.01 sys=0.01, real=0.02 secs]
分别代表 用户态消耗的CPU时间、内核态消耗的CPU时间 和 操作从开始到结束所经过的墙钟时间。墙钟时间包括各种非运算的等待耗时,如IO等待、线程阻塞。CPU时间不包括等待时间,当系统有多核时,多线程操作会叠加这些CPU时间,所以user或sys时间会超过real时间

内存分配策略
首选需要知道不同的垃圾收集器会有不同的体现()
  • 优先分配到eden区
    • 虚拟机参数设置
      -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintGCTimeStamps
    • 测试代码
      public class Neicunfenpei {

      public static void main(String[] args) {
         byte[] b1 = new byte[4*1024];
      }
      }


    • 输出的内存分配
      Heap
      def new generation total 39296K, used 4211K [0x0000000740000000, 0x0000000742aa0000, 0x000000076aaa0000)
      eden space 34944K, 12% used [0x0000000740000000, 0x000000074041cfd0, 0x0000000742220000)
      from space 4352K, 0% used [0x0000000742220000, 0x0000000742220000, 0x0000000742660000)
      to space 4352K, 0% used [0x0000000742660000, 0x0000000742660000, 0x0000000742aa0000)
      tenured generation total 87424K, used 0K [0x000000076aaa0000, 0x0000000770000000, 0x00000007c0000000)
      the space 87424K, 0% used [0x000000076aaa0000, 0x000000076aaa0000, 0x000000076aaa0200, 0x0000000770000000)
      Metaspace used 3086K, capacity 4496K, committed 4864K, reserved 1056768K
      class space used 341K, capacity 388K, committed 512K, reserved 1048576K
    • 我们关注以下eden space 34944K这个参数,总空间34944K,我们分配了4096K,use 了12% 说明我们创建的对象进入到了eden区
  • 大对象直接分配到老年代(什么是大对象呢?一般是指对象所占用的空间比新生代的空间要大)
    • 虚拟机参数设置
      -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintGCTimeStamps
    • 测试代码
      public class Neicunfenpei {

      public static void main(String[] args) {
          byte[] b0 = new byte[30*1024*1024];
      }
      }


    • 输出的内存分配
      Heap
      def new generation total 39296K, used 34931K [0x0000000740000000, 0x0000000742aa0000, 0x000000076aaa0000)
      eden space 34944K, 99% used [0x0000000740000000, 0x000000074221cfe0, 0x0000000742220000)
      from space 4352K, 0% used [0x0000000742220000, 0x0000000742220000, 0x0000000742660000)
      to space 4352K, 0% used [0x0000000742660000, 0x0000000742660000, 0x0000000742aa0000)
      tenured generation total 87424K, used 0K [0x000000076aaa0000, 0x0000000770000000, 0x00000007c0000000)
      the space 87424K, 0% used [0x000000076aaa0000, 0x000000076aaa0000, 0x000000076aaa0200, 0x0000000770000000)
      Metaspace used 3086K, capacity 4496K, committed 4864K, reserved 1056768K
      class space used 341K, capacity 388K, committed 512K, reserved 1048576K
    • 分析以上输出的日志信息
      def new generation total 39296K 新代总的内存是39296K,大约等于30M 我们代码中分配的是30M
      eden space 34944K, 99% 使用率为 99% 任然没有达到新生代内存的最大值,所以不会去到老年代,我们接着把byte数组的长度调大
    • 测试代码
      public class Neicunfenpei {

      public static void main(String[] args) {
          byte[] b0 = new byte[40*1024*1024];
      }
      }


    • 输出的内存分配
      Heap
      def new generation total 39296K, used 4211K [0x0000000740000000, 0x0000000742aa0000, 0x000000076aaa0000)
      eden space 34944K, 12% used [0x0000000740000000, 0x000000074041cfd0, 0x0000000742220000)
      from space 4352K, 0% used [0x0000000742220000, 0x0000000742220000, 0x0000000742660000)
      to space 4352K, 0% used [0x0000000742660000, 0x0000000742660000, 0x0000000742aa0000)
      tenured generation total 87424K, used 40960K [0x000000076aaa0000, 0x0000000770000000, 0x00000007c0000000)
      the space 87424K, 46% used [0x000000076aaa0000, 0x000000076d2a0010, 0x000000076d2a0200, 0x0000000770000000)
      Metaspace used 3091K, capacity 4496K, committed 4864K, reserved 1056768K
      class space used 341K, capacity 388K, committed 512K, reserved 1048576K
    • 分析以上输出的日志信息
      eden space 34944K, 12% used 新生代占用了12%,但是这12%不是我们创建byte数组所占用的,tenured generation total 87424K, used 40960K看老年代,使用了40960K刚好是我们创建的数组的长度,对比之前的可以发现大对象直接分配到了老年代
  • 长期存活的对象分配到老年代
    • 堆中的每一个对象都有自己的年龄,一般情况下,年轻对象存放在年轻代,年老对象存放在年老代,为了做到这一点,虚拟机为每一个对象都维护了一个年龄
    • 如果对象在eden,经过一次Minor GC后还存活,并且能被Survivor区容纳的话,将被移动到Survivor空间中,并且对象年龄设置为1,对象在Survivor空间中每'熬过'一次Minor GC 年龄就会增加1岁,当年龄增加到一定程度(默认是15岁) ,就会被晋升到老年代。
    • 到此大家可能有一个疑问,就是对象才刚被创建怎么就会发生MinorGC呢?
      要对Minor GC、Major GC 、Full GC做一个解释
      从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,可以这里理解比较好记忆morning(早晨)就是年轻。这一定义既清晰又易于理解。但是,当发生MinorGC事件的时候,有一些有趣的地方需要注意到:
      1.当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC
      2.意思就是,在分配内存的时候先分配到eden区,如果eden区空间不够,则会发生Minor GC,之后如果eden区还是不能存下,而刚好Survivor区域可以存下,则会把对象存放到Survivor区域
      Major GC是从老年代回收
      Full GC (Full 全部)是从所有的空间回收,需要Stop The World
    • 虚拟机参数设置
      -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=2 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution
      需要对参数做以下解释
      -Xms20M:最小内存20M,-Xmx20M最大内存,-Xmn10M年轻代内存,-XX:SurvivorRatio年轻代中的eden区和Survivor区的比例,如上设置的为2,说明eden区和Survivor区的比例为2:1,至于怎么记住这些东西,请参考JVM杂谈,理解记忆
    • 测试代码
      public class Neicunfenpei {
         public static void main(String[] args) {
             byte[] b1,b2,b3,b4;

             b1= new byte[1024*512];
             b2 = new byte[1024*1024*2];
             b3 = new byte[1024*1024*4];
             b3 = null;
             b3 = new byte[1024*1024*4];

         }
      }


    • 输出的内存分配日志
      [GC (Allocation Failure) [DefNew
      Desired survivor size 1310720 bytes, new threshold 15 (max 15)
      -age 1: 985528 bytes, 985528 total
      : 4515K->962K(7680K), 0.0034246 secs] 4515K->3010K(17920K), 0.0034523 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
      [GC (Allocation Failure) [DefNew
      Desired survivor size 1310720 bytes, new threshold 15 (max 15)
      -age 2: 963736 bytes, 963736 total
      : 5058K->941K(7680K), 0.0013148 secs] 7106K->2989K(17920K), 0.0013366 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
      Heap
      def new generation total 7680K, used 5139K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      eden space 5120K, 82% used [0x00000007bec00000, 0x00000007bf019b30, 0x00000007bf100000)
      from space 2560K, 36% used [0x00000007bf100000, 0x00000007bf1eb498, 0x00000007bf380000)
      to space 2560K, 0% used [0x00000007bf380000, 0x00000007bf380000, 0x00000007bf600000)
      tenured generation total 10240K, used 2048K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      the space 10240K, 20% used [0x00000007bf600000, 0x00000007bf800010, 0x00000007bf800200, 0x00000007c0000000)
      Metaspace used 3093K, capacity 4496K, committed 4864K, reserved 1056768K
      class space used 341K, capacity 388K, committed 512K, reserved 1048576K
    • 分析输出的内存日志
      -XX:MaxTenuringThreshold=15,表示survivor区的对象年龄增加到15的时候才会进入到老年代,所以我们看到年轻代的from区域占用了36%,并没有进入到老年代,接着测试
    • 虚拟机参数设置
      -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=2 -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution
    • 测试代码
      public class Neicunfenpei {
         public static void main(String[] args) {
             byte[] b1,b2,b3,b4;

             b1= new byte[1024*512];
             b2 = new byte[1024*1024*2];
             b3 = new byte[1024*1024*4];
             b3 = null;
             b3 = new byte[1024*1024*4];

         }
      }


    • 输出的内存分配日志
      [GC (Allocation Failure) [DefNew
      Desired survivor size 1310720 bytes, new threshold 1 (max 1)
      -age 1: 951560 bytes, 951560 total
      : 4409K->929K(7680K), 0.0036465 secs] 4409K->2977K(17920K), 0.0036764 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
      [GC (Allocation Failure) [DefNew
      Desired survivor size 1310720 bytes, new threshold 1 (max 1)
      -age 1: 21904 bytes, 21904 total
      : 5077K->21K(7680K), 0.0015070 secs] 7125K->2998K(17920K), 0.0015301 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
      Heap
      def new generation total 7680K, used 4203K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      eden space 5120K, 81% used [0x00000007bec00000, 0x00000007bf015790, 0x00000007bf100000)
      from space 2560K, 0% used [0x00000007bf100000, 0x00000007bf105590, 0x00000007bf380000)
      to space 2560K, 0% used [0x00000007bf380000, 0x00000007bf380000, 0x00000007bf600000)
      tenured generation total 10240K, used 2977K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      the space 10240K, 29% used [0x00000007bf600000, 0x00000007bf8e8518, 0x00000007bf8e8600, 0x00000007c0000000)
      Metaspace used 3000K, capacity 4496K, committed 4864K, reserved 1056768K
      class space used 330K, capacity 388K, committed 512K, reserved 1048576K
    • 分析输出的内存日志
      -XX:MaxTenuringThreshold=1,表示survivor区的对象年龄增加到1的时候就会进入到老年代,我们可以通过日志看到age刚好为1,所以对象会被分配到老年代,这时候看年轻代的from区很干净,而老年代的内存多占用了9%
      说明对象被分配到了老年代

  • 动态对象年龄判断
    • 对象的年龄到达了MaxTenuringThreshold可以进入老年代,同时,如果在survivor区中相同年龄所有对象大小的总和大于survivor区的一半,年龄大于等于该年龄的对象就可以直接进入老年代。无需等到MaxTenuringThreshold中要求的年龄。
    • 虚拟机参数设置
      -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:MaxTenuringThreshold=15 -XX:SurvivorRatio=2 -XX:+PrintTenuringDistribution
    • 测试代码

      public class Neicunfenpei {

         public static void main(String[] args) {
             byte[] b1,b2,b3,b4;
             b1= new byte[1024*640];
             b2 = new byte[1024*640];
             b3 = new byte[1024*1024*4];
             b3 = null;
             b3 = new byte[1024*1024*4];
         }
      }


    • 输出的内存分配日志
      [GC (Allocation Failure) [DefNew
      Desired survivor size 1310720 bytes, new threshold 1 (max 15)
      -age 1: 1749016 bytes, 1749016 total
      : 3235K->1708K(7680K), 0.0027534 secs] 3235K->1708K(17920K), 0.0027799 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
      [GC (Allocation Failure) [DefNew
      Desired survivor size 1310720 bytes, new threshold 15 (max 15)
      : 5858K->0K(7680K), 0.0019749 secs] 5858K->1684K(17920K), 0.0020036 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
      Heap
      def new generation total 7680K, used 4183K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      eden space 5120K, 81% used [0x00000007bec00000, 0x00000007bf015d28, 0x00000007bf100000)
      from space 2560K, 0% used [0x00000007bf100000, 0x00000007bf100000, 0x00000007bf380000)
      to space 2560K, 0% used [0x00000007bf380000, 0x00000007bf380000, 0x00000007bf600000)
      tenured generation total 10240K, used 1684K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      the space 10240K, 16% used [0x00000007bf600000, 0x00000007bf7a53b8, 0x00000007bf7a5400, 0x00000007c0000000)
      Metaspace used 3093K, capacity 4496K, committed 4864K, reserved 1056768K
      class space used 341K, capacity 388K, committed 512K, reserved 1048576K
    • 分析输出的日志
      -XX:MaxTenuringThreshold=15 ,发现 age增加到了1,survivor空间占用任然是0%,而老年代比预期增加了16%,说明b1,b2对象直接进入了老年代,而没有等到15岁的年龄界限,因为2个对象加起来,已经达到了1280KB,并且他们是相同年龄的,满足同龄对象达到Survivor空间的一般规则,我们可以注释掉一个对象发现另外一个对象不会晋升到老年代

  • 空间分配担保
  • 逃逸分析与栈上分配



2 个回复

倒序浏览
回复 使用道具 举报
感谢分享
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马