• JVM内存分配与回收分析


      java虚拟机的自动化内存可以归结为自动化解决了两个问题,一个是内存分配,一个是内存回收。了解虚拟机的分配与回收机制,能让我们对项目的把控更加有力,尤其是对性能调优时,

    各个参数的设置可能会有意想不到的效果。本文结合事例分析各种场景的回收。

      堆内存区域(不包括永久代)种类:1,eden space(属于新生代-new generation)

              2,survivor space(属于新生代-new generation)

              3,tenured generation(年老代)

      首先解释下各参数的含义。我们可以通过run -> run configuration 中选择argument 中的VM arguments来输入参数调节个参数值。

        -Xms20m : 代表给堆分配20m的初始内存

        -Xmx50m : 代表堆的最大分配内存为50m

        -Xmn10m : 代表给新生代(new generation)分配10m的内存

        -Xss128k:每个线程最大堆栈内存

           -XX:PermSize=10M : 给永久代分配10M内存

        -XX:MaxPermSize=100M:永久代最大内存为100M

               -XX:MaxDirectMemorySize=10M:直接内存最大为10M

               -XX:+PrintGCDetails:控制台输出GC信息

        -XX:SurvivorRatio=8:新生代(new generation)中eden区与survivor区分配的比例为8:1

         -XX:PretenureSizeThreshold=2m:对象内存大小超过这个直接放入年老区(tenured generation)

         -XX:MaxTenuringThreshold=3:代表对象在三次后即第四次GC会进入年老区(tenured generation)

      分配策略一:对象优先在eden中分配 

        配置信息:-Xmx20m -Xms20m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8

        代码:

    public class Allocation {
        
        public static final int _1MB = 1024*1024;
        public static void main(String[]args)
        {
            byte[] allocation1,allocation2,allocation3,allocation4,allocation5;
            
            allocation1 = new byte[4*_1MB];
        }
    }

      执行结果:

    Heap
     def new generation   total 9216K, used 4831K [0x331d0000, 0x33bd0000, 0x33bd0000)
      eden space 8192K,  58% used [0x331d0000, 0x33687fd0, 0x339d0000)
      from space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)
      to   space 1024K,   0% used [0x33ad0000, 0x33ad0000, 0x33bd0000)
     tenured generation   total 10240K, used 0K [0x33bd0000, 0x345d0000, 0x345d0000)
       the space 10240K,   0% used [0x33bd0000, 0x33bd0000, 0x33bd0200, 0x345d0000)
     compacting perm gen  total 12288K, used 145K [0x345d0000, 0x351d0000, 0x385d0000)
       the space 12288K,   1% used [0x345d0000, 0x345f4468, 0x345f4600, 0x351d0000)
        ro space 10240K,  42% used [0x385d0000, 0x38a14240, 0x38a14400, 0x38fd0000)
        rw space 12288K,  54% used [0x38fd0000, 0x39654d58, 0x39654e00, 0x39bd0000)

      我们逐行分析,第二行:显示了,我们给新生代分配了9216K内存(这个由于XX:SurvivorRatio参数不同,会有小幅变动,总体接近10M)

           第三行:显示了eden代分配了8912k,并且58%被使用,这个就是程序中新加入的对象放到了新生代中,刚好是4M

           第四行,第五行,两个值默认是一样,是指survivor区的大小,8192:1024刚好是XX:SurvivorRatio=8的8:1

                       第六行:年老代,10240k,总共分配了20M的堆,新生代占了10M,那么年老代就是10M

           后面的则是永久代信息,这边暂不讨论。

      从上面的代码,执行结果,以及分析中,我们能看出最初的对象是分配在eden space中的。

      当然如果eden满了,则会分配到suvivor space 和tenured generation 中,如:

    public class Allocation {
        
        public static final int _1MB = 1024*1024;
        public static void main(String[]args)
        {
            byte[] allocation1,allocation2,allocation3,allocation4,allocation5;
            
            allocation1 = new byte[2*_1MB];
            allocation2 = new byte[2*_1MB];
            allocation3 = new byte[2*_1MB];
            //第四次分配不足,出发gc
            allocation4 = new byte[4*_1MB];
        }
    }

    执行结果:

    [GC [DefNew: 6716K->378K(9216K), 0.0038688 secs] 6716K->6522K(19456K), 0.0038989 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    Heap
     def new generation   total 9216K, used 4802K [0x331d0000, 0x33bd0000, 0x33bd0000)
      eden space 8192K,  54% used [0x331d0000, 0x33621fa0, 0x339d0000)
      from space 1024K,  36% used [0x33ad0000, 0x33b2e940, 0x33bd0000)
      to   space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)
     tenured generation   total 10240K, used 6144K [0x33bd0000, 0x345d0000, 0x345d0000)
       the space 10240K,  60% used [0x33bd0000, 0x341d0030, 0x341d0200, 0x345d0000)
     compacting perm gen  total 12288K, used 145K [0x345d0000, 0x351d0000, 0x385d0000)
       the space 12288K,   1% used [0x345d0000, 0x345f44a0, 0x345f4600, 0x351d0000)
        ro space 10240K,  42% used [0x385d0000, 0x38a14240, 0x38a14400, 0x38fd0000)
        rw space 12288K,  54% used [0x38fd0000, 0x39654d58, 0x39654e00, 0x39bd0000)

      经历了一次GC,信息中显示了各个内存区域的使用情况

      分配策略二:大对象直接进入老年代

        配置信息:-Xmx20m -Xms20m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3m

        代码:

    public class Allocation {
        
        public static final int _1MB = 1024*1024;
        public static void main(String[]args)
        {
            byte[] allocation1,allocation2,allocation3,allocation4,allocation5;
            
            allocation4 = new byte[4*_1MB];
        }
    }

      执行结果:

    Heap
     def new generation   total 9216K, used 735K [0x331d0000, 0x33bd0000, 0x33bd0000)
      eden space 8192K,   8% used [0x331d0000, 0x33287fc0, 0x339d0000)
      from space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)
      to   space 1024K,   0% used [0x33ad0000, 0x33ad0000, 0x33bd0000)
     tenured generation   total 10240K, used 4096K [0x33bd0000, 0x345d0000, 0x345d0000)
       the space 10240K,  40% used [0x33bd0000, 0x33fd0010, 0x33fd0200, 0x345d0000)
     compacting perm gen  total 12288K, used 145K [0x345d0000, 0x351d0000, 0x385d0000)
       the space 12288K,   1% used [0x345d0000, 0x345f4468, 0x345f4600, 0x351d0000)
        ro space 10240K,  42% used [0x385d0000, 0x38a14240, 0x38a14400, 0x38fd0000)
        rw space 12288K,  54% used [0x38fd0000, 0x39654d58, 0x39654e00, 0x39bd0000)

       从结果中可以看出来,eden space,survivor都未分配内存,而tenured generation 则分配了4096k,即4M大小超过了XX:PretenureSizeThreshold=3M限制,直接进入老年代

      分配策略三:长期存活的对象将进入年老代

        jvm在管理内存时,会把存活时间长的对象放入年老代,没经历一次GC存活下来的的对象,他的age加1,

        参数:-Xmx20m -Xms20m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1

        代码:

    public class Allocation {
        
        public static final int _1MB = 1024*1024;
        public static void main(String[]args)
        {
            byte[] allocation1,allocation2,allocation3,allocation4,allocation5;
            
            allocation1 = new byte[1*_1MB/4];
            allocation2 = new byte[4*_1MB];
            allocation3 = new byte[4*_1MB];
            allocation3 = null;
            allocation3 = new byte[4*_1MB];
        }
    }

        执行结果:

    [GC [DefNew: 4924K->633K(9216K), 0.0030944 secs] 4924K->4729K(19456K), 0.0031222 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC [DefNew: 4893K->0K(9216K), 0.0009591 secs] 8989K->4729K(19456K), 0.0009776 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    Heap
     def new generation   total 9216K, used 4260K [0x331d0000, 0x33bd0000, 0x33bd0000)
      eden space 8192K,  52% used [0x331d0000, 0x335f8fd8, 0x339d0000)
      from space 1024K,   0% used [0x339d0000, 0x339d0088, 0x33ad0000)
      to   space 1024K,   0% used [0x33ad0000, 0x33ad0000, 0x33bd0000)
     tenured generation   total 10240K, used 4729K [0x33bd0000, 0x345d0000, 0x345d0000)
       the space 10240K,  46% used [0x33bd0000, 0x3406e6d0, 0x3406e800, 0x345d0000)
     compacting perm gen  total 12288K, used 144K [0x345d0000, 0x351d0000, 0x385d0000)
       the space 12288K,   1% used [0x345d0000, 0x345f4270, 0x345f4400, 0x351d0000)
        ro space 10240K,  42% used [0x385d0000, 0x38a14240, 0x38a14400, 0x38fd0000)
        rw space 12288K,  54% used [0x38fd0000, 0x39654d58, 0x39654e00, 0x39bd0000)

        从上面中,我能看到,在第二次GC中,第二行 [DefNew: 4893K->0K(9216K) 新生代全部清为0,都放到年老代中去了,因为他们的age已经为2,超过-XX:MaxTenuringThreshold=1的限制。

       当然GC的内存分配与回收策略有很多中,而且对于不同的GC回收器,回收机制也不一定一样,上面讲的三种回收机制,使用较广泛,我们可以在eclipse等ide工具中实践。通过不断的调

    节与实践,会使自己对GC以及JVM更加了解。后边会更新更详细的内存分配机制与JVM原理,请关注。

        

  • 相关阅读:
    洛谷P1033 自由落体 题解
    尴尬
    UVA11988 【Broken Keyboard (a.k.a. Beiju Text)】:题解
    UVA101 The Blocks Problem 题解
    TCP的粘包和拆包问题及解决办法(C#)
    MIPS学习笔记(一)
    MySQL基础(一)
    博客园的标签怎么变了两下???
    nextInt()和nextLine()连用报错
    C++代码雨
  • 原文地址:https://www.cnblogs.com/2015zzh/p/4810638.html
Copyright © 2020-2023  润新知