JVM内存分配与回收学习(2)

版权声明:本文为博主原创文章,转载请注明作者与出处,http://blog.csdn.net/lixingtao0520 https://blog.csdn.net/lixingtao0520/article/details/82901959

1、垃圾收集器什么时候开始回收?

(1)新生代有一个Eden区和两个survivor区(From survivor 和To Survivor),每次使用Eden和其中一个Survivor(From Survivor),创建对象时,首先会将对象放入Eden区,如果放不下就会引发一次发生在新生代的minor GC(清理Eden和From Survivor空间),将存活的对象复制到 To survivor空间中(如果Eden区和From Survivor区存活的对象大于To survivor区的大小,怎么办?答:直接放入老年代区),然后清空Eden和 From survivor区的内存。

(2)大对象以及长期存活的对象直接进入老年区。(大对象怎么判断?创建时,Eden区放不下的对象,都是大对象

(3)当每次执行minor GC的时候应该对要晋升到老年代的对象进行分析,如果这些马上要到老年区的对象的大小超过了老年代的剩余大小,那么执行一次Full GC以尽可能地获得老年代的空间。

2、对什么东西:从GC Roots搜索不到,而且经过一次标记清理之后仍没有复活的对象。

3、做了什么: 

新生代:采用复制算法来回收新生代,新生代中Eden区和From Survivior区两部分的内存不是按照1:1来划分,而是按照一定的比例,默认是8:1。 为什么新生代采用复制算法?新生代中绝大部分对象符合‘朝生夕死’的特点,存活率低,适合用复制算法。在存活率高的情况下,来回复制,效率比较低。老年代对象的存活率比较高,不能用复制算法。

老年代:用“标记-整理”算法。老年代中的对象经过多次GC,存活率高,不适合用复制算法。

永久代:存放Java中的类和加载类的类加载器本身。

4、代码验证

注1:以下代码中的VM参数

-Xmx20m -Xms20m  -XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=d:\dump -XX:+PrintGCDetails -Xmn10M

注2:GC日志说明

GC后方括号内的数据代表 “GC前该内存区域已使用的容量->GC后该内存区域已使用的容量(该内存区域总容量)”

在方括号之外代表的是“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)”。

(1)、情况1:大对象直接放入老年代。

          首先我们验证大对象临界的状态。我们没有配置新生代中Eden与Survivor的比例,那么会取默认值8:1,即Eden区为8M,两个survivor区域为1M。设置a对象大小为8*1000*1024,如下。

package com.xtli.jvm;
public class TestOutOfMemory {
    
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[8*1000*1024];
              //System.out.println(v++);
              v++;
          }
     }
}

代码运行后,如下所示

[GC[DefNew: 876K->430K(9216K), 0.0018120 secs] 876K->430K(19456K), 0.0018792 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
 def new generation   total 9216K, used 8622K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K, 100% used [0x33400000, 0x33c00000, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bac8, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 182K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dbf8, 0x3482dc00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

在创建对象a之前出发一次GC,GC后Eden区足够,将对象a放在了Eden区,此时Eden space used 将近100%,如果a对象设置为9*1024*1024大小时,属于大对象,将会直接放在老年代区域。如下所示

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[9*1024*1024];
              System.out.println(v++);
          }
     }
}

代码运行后,如下所示

0
Heap
 def new generation   total 9216K, used 1040K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  12% used [0x33400000, 0x33504160, 0x33c00000)
  from space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
  to   space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
 tenured generation   total 10240K, used 9216K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,  90% used [0x33e00000, 0x34700010, 0x34700200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc60, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

注意“ tenured generation   total 10240K, used 9216K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,  90% used [0x33e00000, 0x34700010, 0x34700200, 0x34800000)”说明对象直接放入了老年代。

(2)、情况2:Eden区足够,将对象直接放入了Eden区

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[1*1024*1024];
              System.out.println(v++);
          }
     }
}

代码运行后,如下所示,对象直接放入了新生代的Eden区(Eden区还有其他对象,所以所用内存比1024*1024大)

0
Heap
 def new generation   total 9216K, used 2064K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  25% used [0x33400000, 0x33604170, 0x33c00000)
  from space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
  to   space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc60, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

(3)情况3:对象在Eden放不下时,发生GC。

        此次循环产生8个1024*1024的对象,如下

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<8) {
              byte[] a = new byte[1*1024*1024];
              System.out.println(v++);
          }
     }
}

 结果如下

0
1
2
3
4
5
6
[GC[DefNew: 8044K->431K(9216K), 0.0015837 secs] 8044K->431K(19456K), 0.0016484 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
7
Heap
 def new generation   total 9216K, used 1709K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  15% used [0x33400000, 0x3353f960, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bd20, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc60, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

       前7次对象能放入Eden区,未发生GC。但当第8次准备放入Eden区时,发现Eden区内存不够,JVM触发一次GC操作。GC后,对象可以放入Eden区。将存活的对象(431k大小 = 1024k*42%)放入survivor区域。

(4)情况4。对象在Eden区放不下,不会将对象放入了survivor区。而是发生GC。对比情况3.

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<8) {
              if(v==7) {
                   byte[] a = new byte[200*1024];
              } else {
                   byte[] a = new byte[1*1024*1024];
              }
              System.out.println(v++);
          }
     }
}

结果如下

0
1
2
3
4
5
6
[GC[DefNew: 8044K->431K(9216K), 0.0010873 secs] 8044K->431K(19456K), 0.0011343 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
7
Heap
 def new generation   total 9216K, used 885K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,   5% used [0x33400000, 0x33471960, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bd20, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc68, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

从上图看到,如果v=7时,将a的值调整到太大,将会发生GC(不会向survivor区存放),a的值调整到太小(例如new byte[100*1024],如下),对象仍然是存放在Eden区,不会向survivor区域存放。

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<8) {
              if(v==7) {
                   byte[] a = new byte[100*1024];
              } else {
                   byte[] a = new byte[1*1024*1024];
              }
              System.out.println(v++);
          }
     }
}
0
1
2
3
4
5
6
7
Heap
 def new generation   total 9216K, used 8192K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K, 100% used [0x33400000, 0x33c00000, 0x33c00000)
  from space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
  to   space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
 tenured generation   total 10240K, used 0K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   0% used [0x33e00000, 0x33e00000, 0x33e00200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dc68, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

(5)情况5:新建对象时,Eden区内存不够,触发GC。GC过程中,存活的对象大于survivor内存大小,则将存活的对象直接存入老年代区域。

package com.xtli.jvm;
public class TestOutOfMemory {
     
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] b = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] c = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] d = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] e = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] f = new byte[1*1024*1024];
              System.out.println(v++);
              byte[] g = new byte[1*1024*1024];
              System.out.println(v++);
              //======由前面的实例可知,上面7次创建不会发生GC=====下面的创建将会发生GC=====
              byte[] h = new byte[1*1024*1024];
              System.out.println(v++);
          }
     }
}

结果如下

0
1
2
3
4
5
6
[GC[DefNew: 8044K->431K(9216K), 0.0047010 secs] 8044K->7599K(19456K), 0.0047616 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
7
Heap
 def new generation   total 9216K, used 1709K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,  15% used [0x33400000, 0x3353f960, 0x33c00000)
  from space 1024K,  42% used [0x33d00000, 0x33d6bd20, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 7168K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,  70% used [0x33e00000, 0x34500070, 0x34500200, 0x34800000)
 compacting perm gen  total 12288K, used 183K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482dd58, 0x3482de00, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

      第8次创建前,发生了GC,新生代内存由8044K降到431K(GC后存活的对象放到了survivor区域,从之后的from space可以看出 1024k*42% = 431k),堆内存由8044K降到7599K(新生代的431k+老年代7168k),即新生代中保存的对象放到了老年代区域。

(6)情况6:对象放入老年代时,内存空间不够,发生FullGC。

       设置a对象大小为10*1024*1024

package com.xtli.jvm;
public class TestOutOfMemory {
    
     public static void main(String[] args) {
          int v = 0;
          while(v<1) {
              byte[] a = new byte[10*1024*1024];
              //System.out.println(v++);
              v++;
          }
     }
}

结果如下

[GC[DefNew: 876K->430K(9216K), 0.0010839 secs][Tenured: 0K->429K(10240K), 0.0027068 secs] 876K->429K(19456K), [Perm : 182K->182K(12288K)], 0.0038749 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC[Tenured: 429K->419K(10240K), 0.0022318 secs] 429K->419K(19456K), [Perm : 182K->182K(12288K)], 0.0022807 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to d:\dump ...
Unable to create d:\dump: File exists
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
     at com.xtli.jvm.TestOutOfMemory.main(TestOutOfMemory.java:7)
Heap
 def new generation   total 9216K, used 409K [0x33400000, 0x33e00000, 0x33e00000)
  eden space 8192K,   5% used [0x33400000, 0x33466750, 0x33c00000)
  from space 1024K,   0% used [0x33d00000, 0x33d00000, 0x33e00000)
  to   space 1024K,   0% used [0x33c00000, 0x33c00000, 0x33d00000)
 tenured generation   total 10240K, used 419K [0x33e00000, 0x34800000, 0x34800000)
   the space 10240K,   4% used [0x33e00000, 0x33e68db0, 0x33e68e00, 0x34800000)
 compacting perm gen  total 12288K, used 185K [0x34800000, 0x35400000, 0x38800000)
   the space 12288K,   1% used [0x34800000, 0x3482e4c0, 0x3482e600, 0x35400000)
    ro space 10240K,  44% used [0x38800000, 0x38c7c1d8, 0x38c7c200, 0x39200000)
    rw space 12288K,  52% used [0x39200000, 0x398440c0, 0x39844200, 0x39e00000)

放入老年代的对象太大,发生了Full GC(一般Full GC之前会伴有一次GC,由于对象太大,直接OOM了)

猜你喜欢

转载自blog.csdn.net/lixingtao0520/article/details/82901959