JVM内存分配与回收策略

JVM的自动内存管理主要解决两个问题:

  • 给对象分配内存
  • 回收给对象分配的内存

内存分配规则

  • 新生代GC(Minor GC):发生在新生代的垃圾收集动作,Minor GC动作非常频繁,回收速度较快
  • 老年代GC(Major GC/Full GC):发生在老年代的GC,速度比Minor GC慢10倍以上

1.对象优先在Eden分配

  • 当Eden区没有足够内存时,虚拟机将会发起一次Minor GC
  • -XX:+PrintGCDetails 发生内存回收时打印日志

 代码测试

package basicKnowledge.jvm.GC;
/*
*VM args:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
* -Xms:Java堆最小内存
* -Xmx:Java堆最大内存
* -Xmn:新生代内存大小
* -XX:+PrintGCDetails 发生垃圾回收时打印日志
* -XX:SurvivorRatio=8 Eden区与survivor的比例
* -XX:+UseParNewGC 使用ParNew+Serial Old进行内存回收
* 新生代总的可用空间为:8192+1024=9216(Eden+1个survivor)
 */
public class TestAllocation {
    private static int _1MB=1024*1024;

    public static void main(String[] args) {
        byte[] allocation1,allocation2,allocation3,allocation4;
        allocation1=new byte[2*_1MB];
        allocation2=new byte[2*_1MB];
        allocation3=new byte[2*_1MB];
        allocation4=new byte[4*_1MB];//发生垃圾回收(为毛我也不知道)

    }
}

运行结果 

"D:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10485760 "-javaagent:D:\Program Files\ideaIU-2018.3.3.win\lib\idea_rt.jar=61820:D:\Program Files\ideaIU-2018.3.3.win\bin" 
[GC (Allocation Failure) [ParNew: 8157K->652K(9216K), 0.0070980 secs] 8157K->6796K(19456K), 0.0071841 secs] [Times: user=0.03 sys=0.03, real=0.02 secs] 
Heap
 par new generation   total 9216K, used 4830K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
  from space 1024K,  63% used [0x00000000ff500000, 0x00000000ff5a3268, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 6144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  60% used [0x00000000ff600000, 0x00000000ffc00030, 0x00000000ffc00200, 0x0000000100000000)
 Metaspace       used 3223K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

Process finished with exit code 0

2.大对象直接进入老年代

  • 对于数组、长字符串等大对象,直接进入老年代(避免新生代在采用复制算法进行垃圾收集时产生在Eden与两个survivor区产生大量的复制)
  • -XX:PretenureSizeThreshold 大于这个设置值得对象直接在老年代分配

代码测试

package basicKnowledge.jvm.GC;
/*
*VM args: -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=2M
 */
public class TestPretenureSizeThreshold {
    private static int _1MB=1024*1024;

    public static void main(String[] args) {
        byte[] allocation1;
        allocation1=new byte[2* _1MB];


    }
}

运行结果

"D:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=2M "
Heap
 par new generation   total 9216K, used 2177K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  26% used [0x00000000fec00000, 0x00000000fee20558, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 2048K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  20% used [0x00000000ff600000, 0x00000000ff800010, 0x00000000ff800200, 0x0000000100000000)
 Metaspace       used 3222K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

Process finished with exit code 0

3.长期存活的对象将进入老年代

  • 虚拟机给每个对象定义了一个对象年龄(Age)计数器,对象熬过一次垃圾回收,年龄就增加一岁。当年龄增加到一定程度后,将会晋升到老年代。
  • 年龄阈值参数:-XX:MaxTenuringThreshold(默认等于15)

代码测试

package basicKnowledge.jvm.GC;

public class TestTenuringThreshold {
    private static int _1MB=1024*1024;

    public static void main(String[] args) {
        byte[] allocation1,allocation2,allocation3,allocation4;
        allocation1=new byte[_1MB/4];
        allocation2=new byte[4*_1MB];
        allocation3=new byte[4*_1MB];//第一次GC
        allocation3=null;
        allocation3=new byte[4*_1MB];//第二次GC


    }
}

运行结果 

"D:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 
[GC (Allocation Failure) [ParNew: 6365K->904K(9216K), 0.0131549 secs] 6365K->5000K(19456K), 0.0133888 secs] [Times: user=0.03 sys=0.01, real=0.01 secs] 
[GC (Allocation Failure) [ParNew: 5000K->0K(9216K), 0.0131331 secs] 9096K->9097K(19456K), 0.0132495 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
Heap
 par new generation   total 9216K, used 4178K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 9097K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  88% used [0x00000000ff600000, 0x00000000ffee25e8, 0x00000000ffee2600, 0x0000000100000000)
 Metaspace       used 3223K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

Process finished with exit code 0

可以看到第二次GC时,新生代的内存变为0K 

虚拟机并不总是要求对象的年龄达到阈值后才能进入老年代。如果在survivor空间中相同年龄放入所有对象大小的总和大于survivor空间的一半,大于或等于该年龄 //的对象就可以直接进入老年代,不必要求对象年龄一定达到阈值

代码测试

package basicKnowledge.jvm.GC;
//如果在survivor空间中相同年龄放入所有对象大小的总和大于survivor空间的一半,大于或等于该年龄
//的对象就可以直接进入老年代,不必要求对象年龄一定达到阈值
public class TestTenuringThreshold2 {
    private static int _1MB=1024*1024;

    public static void main(String[] args) {
        byte[] allocation1,allocation2,allocation3,allocation4,allocation5;
        allocation1=new byte[_1MB/4];
//        allocation2=new byte[_1MB/4];
        allocation3=new byte[4*_1MB];
        allocation4=new byte[4*_1MB];//此时发生第一次垃圾回收,GC期间发现allocation3无法放入
                                    //survivor空间,通过分配担保机制提前转移至老年代。此时allocation4
                                    //分配到Eden区
//        allocation4=null;
        allocation5=new byte[4*_1MB];//此时发生第二次GC,GC期间allocation4通过分配担保机制进入老年代
                                    //allocation1通过对象年龄标记进入老年代,allocation5进入Eden区



    }
}

 运行结果

"C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+UseParNewGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 "basicKnowledge.jvm.GC.TestTenuringThreshold2
[GC (Allocation Failure) [ParNew: 6365K->913K(9216K), 0.0021156 secs] 6365K->5009K(19456K), 0.0021523 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [ParNew: 5009K->0K(9216K), 0.0020255 secs] 9105K->9099K(19456K), 0.0020414 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 9216K, used 4178K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 9099K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  88% used [0x00000000ff600000, 0x00000000ffee2ff0, 0x00000000ffee3000, 0x0000000100000000)
 Metaspace       used 3216K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 349K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

Process finished with exit code 0

4.空间分配担保

在发生Minor GC前,虚拟机会先检查老年代的最大连续可用空间是否大于新生代所有对象总空间或历次晋升的平均大小。如果小于,将进行Full GC

 注:本文所有内容均基于《深入理解Java虚拟机》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值