java堆内存分配与回收策略

0 概述

Java技术体系动态内存管理也就是给对象分配内存以及回收分配给对象内存;这一点和传统的C/C++技术体系有着很大的区别。本文主要讲述java堆内存分配与回收策略。

1 对象分配与回收策略

下图给出java堆内存结构,其分为两大块区域:新生代、老年代。其中新生代又包含三个区域:一个Eden区和两个Survivor区,由于在发生Minor GC时候会把存活的对象拷贝到另一个Survivor区上,因此也称为from区和to区 。

  • 对象优先在Eden区域分配,大对象直接进入老年代;之所以大对象直接进入老年代的目的是为了避免Eden和Survivor的互相拷贝大对象。虚拟机提供了-XX:PretenureSizeThreshold(该设置只对Serial和ParNew收集器生效)参数,大于该参数设置值的对象将直接在老年代分配
  • 长期存活的对象会进入老年代;如果对象在Eden分配后并经过一次Minor GC(Young GC)依然存活,并且能被Survivor区域容纳,将对象复制到Survivor区域,同时将对象的年龄设置为1,对象在Survivor经历过一次Minor GC,年龄增加到一定程度(默认是15岁),就会晋升到老年代。这个阈值-XX:MaxTenuringThreshold来设置相关阈值。
    java堆内存分配与回收策略_第1张图片

2 实例分析

  • 对象优先分配在eden区实例分析:
public class EdenTest {

    private static final int _1MB=1024*1024;

    /**
     * VM -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
     * @param args
     */
    public static void main(String[] args) throws Exception{
        //通GC日志可以看到对象优先分配到Eden区域
        byte[] bytes=new byte[_1MB*4];
    }
}

通过GC 日志可以看出eden区域空间已经有77%使用,from to 区域也就是两个Survivor区域大小没有减少,老年代的空间也没有减少。
Heap
 PSYoungGen      total 9216K, used 6322K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 8192K, 77% used [0x00000007bf600000,0x00000007bfc2cba0,0x00000007bfe00000)
  from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
  to   space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
 ParOldGen       total 10240K, used 0K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  object space 10240K, 0% used [0x00000007bec00000,0x00000007bec00000,0x00000007bf600000)
 Metaspace       used 3313K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 369K, capacity 388K, committed 512K, reserved 1048576K
  • 大对象直接分配到老年代实例分析
/**
 * Created by apple on 17/10/31.
 */
public class EdenTest {

    private static final int _1MB = 1024 * 1024;

    /**
     * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails
     *  -XX:PretenureSizeThreshold=4194304 -XX:+UseSerialGC
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {
        //对象直接分配到老年代
        byte[] bytes1 = new byte[4*_1MB];
    }


}
从GC日志可以看出大对象直接分配到tenured区域(老年代)
Heap
 def new generation   total 9216K, used 2226K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  eden space 8192K,  27% used [0x00000007bec00000, 0x00000007bee2cb90, 0x00000007bf400000)
  from space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
  to   space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
 tenured generation   total 10240K, used 4096K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
   the space 10240K,  40% used [0x00000007bf600000, 0x00000007bfa00010, 0x00000007bfa00200, 0x00000007c0000000)
 Metaspace       used 3299K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 368K, capacity 388K, committed 512K, reserved 1048576K
  • 长期存活的对象将进入老年代
public class EdenTest {

    private static final int _1MB = 1024 * 1024;

    /**
     *-verbose:gc -Xms200M -Xmx200M -Xmn100M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseSerialGC
     *  -XX:MaxTenuringThreshold=1 
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {


        byte[] bytes1 = new byte[4*_1MB];
        byte[] bytes2 = new byte[50 * _1MB];
        bytes2 = null;
        bytes2 = new byte[50 * _1MB];

        bytes2 = null;
        bytes2 = new byte[50 * _1MB];

    }

}

  -XX:MaxTenuringThreshold=1 时候
[GC (Allocation Failure) [DefNew: 60211K->4523K(92160K), 0.0045386 secs] 60211K->4523K(194560K), 0.0045717 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [DefNew: 55723K->0K(92160K), 0.0037334 secs] 55723K->4511K(194560K), 0.0037593 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 92160K, used 52019K [0x00000007b3800000, 0x00000007b9c00000, 0x00000007b9c00000)
  eden space 81920K,  63% used [0x00000007b3800000, 0x00000007b6acce50, 0x00000007b8800000)
  from space 10240K,   0% used [0x00000007b8800000, 0x00000007b8800000, 0x00000007b9200000)
  to   space 10240K,   0% used [0x00000007b9200000, 0x00000007b9200000, 0x00000007b9c00000)
 tenured generation   total 102400K, used 4511K [0x00000007b9c00000, 0x00000007c0000000, 0x00000007c0000000)
   the space 102400K,   4% used [0x00000007b9c00000, 0x00000007ba067e70, 0x00000007ba068000, 0x00000007c0000000)
 Metaspace       used 3313K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 369K, capacity 388K, committed 512K, reserved 1048576K

  -XX:MaxTenuringThreshold=15 时候
[GC (Allocation Failure) [DefNew: 60211K->4523K(92160K), 0.0043589 secs] 60211K->4523K(194560K), 0.0043856 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [DefNew: 55723K->4511K(92160K), 0.0045079 secs] 55723K->4511K(194560K), 0.0045374 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 92160K, used 58169K [0x00000007b3800000, 0x00000007b9c00000, 0x00000007b9c00000)
  eden space 81920K,  65% used [0x00000007b3800000, 0x00000007b6c66800, 0x00000007b8800000)
  from space 10240K,  44% used [0x00000007b8800000, 0x00000007b8c67e70, 0x00000007b9200000)
  to   space 10240K,   0% used [0x00000007b9200000, 0x00000007b9200000, 0x00000007b9c00000)
 tenured generation   total 102400K, used 0K [0x00000007b9c00000, 0x00000007c0000000, 0x00000007c0000000)
   the space 102400K,   0% used [0x00000007b9c00000, 0x00000007b9c00000, 0x00000007b9c00200, 0x00000007c0000000)
 Metaspace       used 3313K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 369K, capacity 388K, committed 512K, reserved 1048576K

3 GC触发的条件

GC 触发的条件:

  • MinorGC(Young GC)是指发生在新生代的垃圾收集动作,因为java 对象大多数都具备朝生夕灭的特性,所以YoungGC一般比较频繁,一般回收的速度也比较快。触发条件:Eden空间不足以分配内存给新的对象。
  • Major GC(Full GC)指发生在老年代GC,出现Full GC 一般会伴有Yong GC,Full GC 速度一般比Young GC慢10倍以上。触发条件: 老生代空间不足,新生代对象或者大对象无法转入老生代,大对象一般需要大的连续空间,如果直接进入老年代,很容易出现Full GC,因此避免短存活期的大对象存在.

参考文献
[1] 深入理解java 虚拟机(第二版),周志明著

你可能感兴趣的:(jvm)