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();
        }
        
    }
  • 相关阅读:
    二分练习题4 查找最接近的元素 题解
    二分练习题5 二分法求函数的零点 题解
    二分练习题3 查找小于x的最大元素 题解
    二分练习题2 查找大于等于x的最小元素 题解
    二分练习题1 查找元素 题解
    code forces 1176 D. Recover it!
    code forces 1173 B. Nauuo and Chess
    code forces 1173 C. Nauuo and Cards
    吴恩达深度学习课程笔记-15
    吴恩达深度学习课程笔记-14
  • 原文地址:https://www.cnblogs.com/guchunchao/p/10522900.html
Copyright © 2011-2022 走看看