zoukankan      html  css  js  c++  java
  • 【七】内存分配策略

    JVM参数设置、分析

    内存分配策略

    • 优先分配到Eden区
    • 大对象直接分配到老年代
      •   -XX:PretenureSizeThreshold  设定大对象内存大小阈值
      • 一般认为大字符串,数组,为大对象
      • 因为新生代频繁发生垃圾回收,且采用复制算法,若是 频繁复制大对象,影响效率。
    • 长期存活的对象分配到老年代
    • 空间分配担保
    • 动态对象年龄判断

     

    -verbose:gc -XX:+PrintGCDetails

    由如下信息可判断,垃圾收集器新生代肯定使用的是Parellel,老年代可能是GMS

    [GC (System.gc()) [PSYoungGen: 693K->464K(6144K)] 693K->472K(19968K), 0.0035897 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (System.gc()) [PSYoungGen: 464K->0K(6144K)] [ParOldGen: 8K->329K(13824K)] 472K->329K(19968K), [Metaspace: 2776K->2776K(1056768K)], 0.0042550 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    Heap
     PSYoungGen      total 6144K, used 56K [0x00000007bf980000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 5632K, 1% used [0x00000007bf980000,0x00000007bf98e2b8,0x00000007bff00000)
      from space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
      to   space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
     ParOldGen       total 13824K, used 329K [0x00000007bec00000, 0x00000007bf980000, 0x00000007bf980000)
      object space 13824K, 2% used [0x00000007bec00000,0x00000007bec52408,0x00000007bf980000)
     Metaspace       used 2782K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 293K, capacity 386K, committed 512K, reserved 1048576K

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC

    0
    [Full GC (System.gc()) [Tenured: 0K->329K(13696K), 0.0027315 secs] 787K->329K(19840K), [Metaspace: 2777K->2777K(1056768K)], 0.0027869 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    Heap
     def new generation   total 6144K, used 55K [0x00000007bec00000, 0x00000007bf2a0000, 0x00000007bf2a0000)  (serial 垃圾收集器的新生代的名称)
      eden space 5504K,   1% used [0x00000007bec00000, 0x00000007bec0dda0, 0x00000007bf160000)
      from space 640K,   0% used [0x00000007bf160000, 0x00000007bf160000, 0x00000007bf200000)
      to   space 640K,   0% used [0x00000007bf200000, 0x00000007bf200000, 0x00000007bf2a0000)
     tenured generation   total 13696K, used 329K [0x00000007bf2a0000, 0x00000007c0000000, 0x00000007c0000000)
       the space 13696K,   2% used [0x00000007bf2a0000, 0x00000007bf2f2408, 0x00000007bf2f2600, 0x00000007c0000000)
     Metaspace       used 2783K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 293K, capacity 386K, committed 512K, reserved 1048576K

    由此可见jdk1.8默认使用的是Parellel,但是并不是任何环境下都要使用Parellel。应该根据JDK所处的一个环境指定。如果环境是一个服务的server,那么默认指定为Parellel,如果是客户端,收集的内存比较小,停顿时间可观,性能高的情况下,我们一般情况下会使用Serial收集器。

    guchunchaodeMacBook-Air:workspaces guchunchao$ java -version
    java version "1.8.0_181"
    Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

    检测内存大于2个G,而且是多核环境,那么默认就认为是Server端。

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8  

    -Xms  初始堆大小

    -Xmx  最大堆大小

    -Xmn  年轻代大小

     -XX:SurvivorRatioEden / Survivor 区的大小比值

     

    public class TestHeap {
        public static final int M = 1024 * 1024;
        public static void main(String[] args) {
            for(int i = 0; i < 7; i++) {
                byte[] b = new byte[10 * M];
            }
        }
    }

    结果:

    [GC (Allocation Failure) [DefNew: 859K->308K(9216K), 0.0012388 secs][Tenured: 0K->307K(10240K), 0.0021830 secs] 859K->307K(19456K), [Metaspace: 2647K->2647K(1056768K)], 0.0034872 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    [Full GC (Allocation Failure) [Tenured(老年代): 307K->295K(10240K)(10M), 0.0016707 secs] 307K->295K(19456K), [Metaspace: 2647K->2647K(1056768K)], 0.0017284 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at com.chaoyijuechen.easypoi.TestHeap.main(TestHeap.java:9)
    Heap
     def new generation   total 9216K, used 246K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      eden space 8192K,   3% used [0x00000007bec00000, 0x00000007bec3d890, 0x00000007bf400000)
      from space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
      to   space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
     tenured generation   total 10240K, used 295K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
       the space 10240K,   2% used [0x00000007bf600000, 0x00000007bf649eb8, 0x00000007bf64a000, 0x00000007c0000000)
     Metaspace       used 2679K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 289K, capacity 386K, committed 512K, reserved 1048576K

    最大堆内存大小才20M,新生代10M,老年代10M,而类中运行for体内的的对象为10M被视为大对象(已经等于新生代的大小了,不能可丁可卯)所以扔到老年代而老年代10M也放不下,因而发生了OutOfMemory.若是Eden区内存设置为:13M时,这是新生代就能放下了,每放一个发生一次垃圾回收,要不然没有剩余空间放。

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn13M

    [GC (Allocation Failure) [DefNew: 884K->308K(12032K), 0.0028284 secs] 884K->308K(19200K), 0.0031123 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [DefNew: 10548K->306K(12032K), 0.0088254 secs] 10548K->306K(19200K), 0.0088818 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [GC (Allocation Failure) [DefNew: 10762K->306K(12032K), 0.0006648 secs] 10762K->306K(19200K), 0.0007101 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [DefNew: 10546K->306K(12032K), 0.0006589 secs] 10546K->306K(19200K), 0.0006899 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [DefNew: 10546K->306K(12032K), 0.0010204 secs] 10546K->306K(19200K), 0.0010774 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [GC (Allocation Failure) [DefNew: 10546K->306K(12032K), 0.0005397 secs] 10546K->306K(19200K), 0.0005737 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    [GC (Allocation Failure) [DefNew: 10546K->306K(12032K), 0.0006790 secs] 10546K->306K(19200K), 0.0007142 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    Heap
     def new generation   total 12032K, used 10762K [0x00000007bec00000, 0x00000007bf900000, 0x00000007bf900000)
      eden space 10752K,  97% used [0x00000007bec00000, 0x00000007bf635db0, 0x00000007bf680000)
      from space 1280K,  23% used [0x00000007bf7c0000, 0x00000007bf80cb30, 0x00000007bf900000)
      to   space 1280K,   0% used [0x00000007bf680000, 0x00000007bf680000, 0x00000007bf7c0000)
     tenured generation   total 7168K, used 0K [0x00000007bf900000, 0x00000007c0000000, 0x00000007c0000000)
       the space 7168K,   0% used [0x00000007bf900000, 0x00000007bf900000, 0x00000007bf900200, 0x00000007c0000000)
     Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

    针对10M的对象共发生了6次新生代的垃圾回收,最后一次for循环产生的对象留在了Eden区,没有其他对象与其竞争堆空间,所以没有发生第7次GC

     为什么年轻代的值由10 -> 13后就没有发生OutOfMemory呢?因为此时Eden区能放得下了,而10M时新生代放不下,只能当成大对象扔到老年代,老年代也放不下,所以就报了OutOfMemory异常。

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8  

    byte[] b = new byte[8 * M];  发生6次GC ,tenured generation(老年代),the space 10240K,  82% used
    byte[] b = new byte[9 * M];   发生6次GC ,tenured generation(老年代),the space 10240K,  92% used  (此时一个对象的空间大小以及大于)
    byte[] b = new byte[10 * M]; 对象被视为大对象扔到老年代,老年代也放不下,所以报OutOfMemory异常




    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8    Eden / survivor = 8

    public class TestHeap {
        public static final int M = 1024 * 1024;
        public static void main(String[] args) {
                byte[] a = new byte[2 * M];
                byte[] b = new byte[2 * M];
                byte[] c = new byte[2 * M];
                byte[] d = new byte[4 * M];
        }
    }

    结果:

    [GC (Allocation Failure) [DefNew: 7003K->308K(9216K), 0.0142125 secs] 7003K->6452K(19456K), 0.0143851 secs] [Times: user=0.01 sys=0.01, real=0.02 secs] 
    Heap
     def new generation   total 9216K, used 4487K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      eden space 8192K,  51% used [0x00000007bec00000, 0x00000007bf014930, 0x00000007bf400000)
      from space 1024K,  30% used [0x00000007bf500000, 0x00000007bf54d2d8, 0x00000007bf600000)
      to   space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
     tenured generation   total 10240K, used 6144K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
       the space 10240K,  60% used [0x00000007bf600000, 0x00000007bfc00030, 0x00000007bfc00200, 0x00000007c0000000)
     Metaspace       used 2654K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

    设置Eden区和survivor区的比值,并手动回收老年代:System.gc()

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 

    public class TestHeap {
        public static final int M = 1024 * 1024;
        public static void main(String[] args) {
                byte[] a = new byte[2 * M];
                byte[] b = new byte[2 * M];
                byte[] c = new byte[2 * M];
                byte[] d = new byte[4 * M];
                System.gc();
        }
    }

    结果:

    [GC (Allocation Failure) [DefNew: 7003K->308K(9216K)新生代内存被回收, 0.0131855 secs] 7003K->6452K(19456K), 0.0133545 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    [Full GC (System.gc()) [Tenured: 6144K->6144K(10240K), 0.0021979 secs] 10708K->10546K(19456K), [Metaspace: 2647K->2647K(1056768K)], 0.0022415 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
    Heap
     def new generation   total 9216K, used 4730K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      eden space 8192K,  57% used [0x00000007bec00000, 0x00000007bf09e8e0, 0x00000007bf400000)  大概是4M   ----> 对象d
      from space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)  因为System.gc(),所以这部分被回收掉了,变成了0%
      to   space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
     tenured generation   total 10240K, used 6144K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
       the space 10240K,  60% used [0x00000007bf600000, 0x00000007bfc00030, 0x00000007bfc00200, 0x00000007c0000000)  大概是6M  --->  对象a,b,c
     Metaspace       used 2654K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

     

    证明:System.gc(); 垃圾回收时,将from space 所指的区域给回收了。

    1、[GC (Allocation Failure):新生代
    新生代总共有10M,Eden区8M,survivor1:1M,suvivor2:1M. 对象a,b,c(都为2M)被扔到Eden区,8M占了6M,此时Eden还剩2M,当将d(4M)往Eden区扔的时候,发现Eden区不够了,此时VM就自动发生了一次垃圾收集:
    [GC (Allocaltion Failure)。这种GC也叫MannerGC ,这是发生在新生代的GC,新生代对象是朝生夕死,存活的不多,是垃圾收集器重点光顾的地区。所以这种GC操作经常发生。特点是相较于Full GC执行时间短。
    2、[Full GC (System.gc()) :老年代
      1)、手动发生:System.gc();
      2)、系统自动发生
      3)、调用频率比gc小得多。老年代的内存中的大对象存活几率非常长,所以Full GC 发生的频率不大。
      4)、执行的时间比较长,耗费的性能可能是GC的10倍以上。

       

      

     非大对象被扔进Eden区

    public class TestHeap {
        
        public static final int M = 1024 * 1024;
        public static void main(String[] args) {
            byte[] b = new byte[10 * M];
        }
    }

    vm参数:

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC   

    Heap
     def new generation   total 39296K, used 12337K [0x0000000740000000, 0x0000000742aa0000, 0x000000076aaa0000)
      eden space 34944K,  35% used [0x0000000740000000, 0x0000000740c0c430, 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 2653K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

    10M 大小的对象 并不被视为大对象,只在Eden区,不直接进老年代

     设置大对象阈值

    -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -XX:PretenureSizeThreshold=10M

    Heap
     def new generation   total 39296K, used 2097K [0x0000000740000000, 0x0000000742aa0000, 0x000000076aaa0000)
      eden space 34944K,   6% used [0x0000000740000000, 0x000000074020c420, 0x0000000742220000)
      from space 4352K,   0% used [0x0000000742220000, 0x0000000742220000, 0x0000000742660000)
      to   space 4352K,   0% used [0x0000000742660000, 0x0000000742660000, 0x0000000742aa0000)
     tenured generation   total 87424K, used 10240K [0x000000076aaa0000, 0x0000000770000000, 0x00000007c0000000)
       the space 87424K,  11% used [0x000000076aaa0000, 0x000000076b4a0010, 0x000000076b4a0200, 0x0000000770000000)
     Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

    因为设置了大对象阈值为10M,所以再次跑程序,被扔进了老年代中

    长期存活的对象分配到老年代

    -XX:MaxTenuringThreshold   默认是15

    每发生一次GC,对象若是没被清理,对象便从Eden到Suvivor0,再在Survivor0和Survivor1之间来回复制。每发生一次位移,Age就+1。直至Age = 15时依然没有被回收掉,便被扔到老年代。

    查看策略:建立一个小对象,手动垃圾回收,看回收多少次后对象被扔到老年代。

    (“注意:1.6之前OK,1.7&1.8可能age到 2,3次就被扔到老年代了”)待验证

    调用一次System.gc()便被扔到了老年代。 ????? 这个参数有待考究。

    空间分配担保

    -XX:+HandlePromotionFailure

    逃逸分析与栈上分配

    逃逸分析:分析对象的作用阈

    如果一个对象被定义在方法体内部后,那么他的受访问权限仅限于方法体内,一旦其引用外部成员后,那么这个对象就发送了逃逸。

    如果这个对象仅仅在方法体内部有效,就认为没有逃逸,就可以把这个对象分配到栈上;否则不分配到栈上。

    public class TestAllocation {
        
        public TestAllocation obj;
        
        /**方法返回TestAllocation对象,发送逃逸*/
        public TestAllocation getInstance() {
            return obj == null ? new TestAllocation() : obj;
        }
    
        /**为成员属性赋值,发生逃逸*/
        public void setObj(TestAllocation obj) {
            this.obj = new TestAllocation();
        }
        
        /**对象仅仅在本方法中使用,没有发生逃逸*/
        public void useObject() {
            TestAllocation obj = new TestAllocation();
        }
        
        /**引用成员变量的值,发生逃逸*/
        public void useObject2() {
            TestAllocation obj = getInstance();
        }
        
    }
  • 相关阅读:
    PHP的后期静态绑定
    php的clone 浅拷贝
    python 从文件导入分类
    Yii2 主从 数据库
    什么是 jsonp ?
    为speedphp最新版添加 仿Yii 的简易版 数据验证 支持不同场景,自定义回调
    redis入门指南-安装redis
    composer -vvv
    依赖注入
    yii2-user
  • 原文地址:https://www.cnblogs.com/guchunchao/p/10522900.html
Copyright © 2011-2022 走看看