java虚拟机系列:内存分配和回收策略

对象的内存分配,往大方向讲,就是在堆上分配(但也可能经过JIT编译后被拆散为标量类型并间接地栈上分配),对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。

TLAB

内存分配的动作,可以按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。哪个线程需要分配内存,就在哪个线程的TLAB上分配。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。这么做的目的之一,也是为了并发创建一个对象时,保证创建对象的线程安全性。TLAB比较小,直接在TLAB上分配内存的方式称为快速分配方式,而TLAB大小不够,导致内存被分配在Eden区的内存分配方式称为慢速分配方式。
###对象优先在Eden分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时候,虚拟机讲进行一次Minor gc。


public class EdenAllocationTest {


    private static final 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]; // 

    }
}

jvm option:-verbose:gc -Xms20M -Xms20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

输出:

Heap
PSYoungGen total 9216K, used 7875K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 96% used [0x00000007bf600000,0x00000007bfdb0f00,0x00000007bfe00000)
from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
to space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
ParOldGen total 10240K, used 4096K [0x00000006cb200000, 0x00000006cbc00000, 0x00000007bf600000)
object space 10240K, 40% used [0x00000006cb200000,0x00000006cb600010,0x00000006cbc00000)
Metaspace used 3067K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 336K, capacity 388K, committed 512K, reserved 1048576K
在分配allocation4时候,发现Eden已经使用了6M,深入空间已经不能够分配allocation4。而且Survivor也无法放下6M的存活数据,所以发生一次monor GC.

Minor GC,Major GC、Full GC
  1. Minor GC:指发生在新生代的垃圾收集动作,非常频繁,速度较快。
  2. Major GC:指发生在老年代的GC,出现Major GC,经常会伴随一次Minor GC,同时Minor GC也会引起Major
    GC,一般在GC日志中统称为GC,不频繁。
  3. Full GC:指发生在老年代和新生代的GC,速度很慢,需要Stop The World。

大对象直接进入老年代

需要大量连续空间的java对象称为大对象。经常出现大对象会导致内存还有不少空间就提前触发了垃圾收集以及获取足够的连续空间来安置它们。
虚拟机提供一个-XX:PretenureSizeThreshold参数,来使大于这个设置值的对象直接在老年代分配。这样做的目的是避免在Eden以及两个Survivor区件发生大量的内存复制。

public class PretenureSizeThreSholdTest {

    static int _1MB = 1024 * 1024;
    public static void main(String[] args) {

        byte[] allocation = new byte[4 * _1MB];
    }
}

jvm options:-verbose:gc -XX:+UseSerialGC -Xms20M -Xms20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=345728

Heap
def new generation total 9216K, used 1731K [0x00000006cb200000, 0x00000006cbc00000, 0x00000006cbc00000)
eden space 8192K, 21% used [0x00000006cb200000, 0x00000006cb3b0ed0, 0x00000006cba00000)
from space 1024K, 0% used [0x00000006cba00000, 0x00000006cba00000, 0x00000006cbb00000)
to space 1024K, 0% used [0x00000006cbb00000, 0x00000006cbb00000, 0x00000006cbc00000)
tenured generation total 10240K, used 4096K [0x00000006cbc00000, 0x00000006cc600000, 0x00000007c0000000)
the space 10240K, 40% used [0x00000006cbc00000, 0x00000006cc000010, 0x00000006cc000200, 0x00000006cc600000)
Metaspace used 3067K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 336K, capacity 388K, committed 512K, reserved 1048576K
PretenureSizeThreshold参数只对Serial和ParNew两款收集器有效,Parallel Scavenge收集器不认识这个参数,Parallel Scavenge收集器一般并不需要设置。如果遇到必须使用此参数的场合,可以考虑ParNew加CMS的收集器组合。

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

虚拟机采用分代收集的思想来管理内存,虚拟机给每个对象定义个一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

public class TenuringThresholdTest {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {

        byte[] allocation1,allocation2,allocation3;
        allocation1 = new byte[_1MB / 4];
        allocation2 = new byte[4 *_1MB];
        allocation3 = new byte[4 *_1MB];
        allocation3 = null;
        allocation3 = new byte[4 *_1MB]; //

    }
}

jvm options:-verbose:gc -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1

gc输出:


 def new generation   total 9216K, used 6363K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  77% used [0x00000000fec00000, 0x00000000ff236fb8, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 2936K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNew: 6363K->716K(9216K), 0.0039814 secs] 6363K->4812K(19456K), 0.0040252 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap after GC invocations=1 (full 0):
 def new generation   total 9216K, used 716K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   0% used [0x00000000fec00000, 0x00000000fec00000, 0x00000000ff400000)
  from space 1024K,  70% used [0x00000000ff500000, 0x00000000ff5b33b8, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 2936K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=1 (full 0):
 def new generation   total 9216K, used 4812K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  50% used [0x00000000fec00000, 0x00000000ff000010, 0x00000000ff400000)
  from space 1024K,  70% used [0x00000000ff500000, 0x00000000ff5b33b8, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 2936K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNewDisconnected from the target VM, address: '127.0.0.1:34191', transport: 'socket'
: 4812K->0K(9216K), 0.0041985 secs] 8908K->4724K(19456K), 0.0042207 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap after GC invocations=2 (full 0):
 def new generation   total 9216K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   0% used [0x00000000fec00000, 0x00000000fec00000, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4724K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  46% used [0x00000000ff600000, 0x00000000ffa9d008, 0x00000000ffa9d200, 0x0000000100000000)
 Metaspace       used 2936K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
}
Heap
 def 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 4724K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  46% used [0x00000000ff600000, 0x00000000ffa9d008, 0x00000000ffa9d200, 0x0000000100000000)
 Metaspace       used 2943K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K

当:jvm options:-verbose:gc -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15


{Heap before GC invocations=0 (full 0):
 def new generation   total 9216K, used 5919K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  72% used [0x00000000fec00000, 0x00000000ff1c7e20, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 3061K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 335K, capacity 388K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNew: 5919K->685K(9216K), 0.0069245 secs] 5919K->4781K(19456K), 0.0069489 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap after GC invocations=1 (full 0):
 def new generation   total 9216K, used 685K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   0% used [0x00000000fec00000, 0x00000000fec00000, 0x00000000ff400000)
  from space 1024K,  66% used [0x00000000ff500000, 0x00000000ff5ab5d8, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 3061K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 335K, capacity 388K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=1 (full 0):
 def new generation   total 9216K, used 4781K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  50% used [0x00000000fec00000, 0x00000000ff000010, 0x00000000ff400000)
  from space 1024K,  66% used [0x00000000ff500000, 0x00000000ff5ab5d8, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 3061K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 335K, capacity 388K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNew: 4781K->0K(9216K), 0.0033854 secs] 8877K->4758K(19456K), 0.0034076 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap after GC invocations=2 (full 0):
 def new generation   total 9216K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   0% used [0x00000000fec00000, 0x00000000fec00000, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4758K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  46% used [0x00000000ff600000, 0x00000000ffaa59c0, 0x00000000ffaa5a00, 0x0000000100000000)
 Metaspace       used 3061K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 335K, capacity 388K, committed 512K, reserved 1048576K
}
Heap
 def 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 4758K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  46% used [0x00000000ff600000, 0x00000000ffaa59c0, 0x00000000ffaa5a00, 0x0000000100000000)
 Metaspace       used 3067K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 336K, capacity 388K, committed 512K, reserved 1048576K

以-XX:MaxTenuringThreshold=1和-XX:MaxTenuringThreshold=15两种设置来执行下面代码中的main()方法,此方法中的allocation1对象需要256KB内存,Survivor空间可以容纳。当MaxTenuringThreshold=1时,allocation1对象在第二次GC发生时进入老年代,新生代已使用的内存GC后非常干净地变成0KB。而MaxTenuringThreshold=15时,第二次GC发生后,allocation1对象则还留在新生代Survivor空间,这时新生代仍然有404KB被占用

动态对象年龄判定

为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

public class TenuringThresholdTest2 {


    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {

        byte[] allocation1,allocation2,allocation3, allocation4;
        allocation1 = new byte[_1MB / 4];
        allocation2 = new byte[_1MB / 4];
        allocation3 = new byte[4 *_1MB];
        allocation4 = new byte[4 *_1MB]; //
        allocation4 = null;
        allocation4 = new byte[4 *_1MB]; //

    }
}

jvm options:-verbose:gc -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:+PrintGC -XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15

jc 输出:


{Heap before GC invocations=0 (full 0):
 def new generation   total 9216K, used 6620K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  80% used [0x00000000fec00000, 0x00000000ff277028, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,   0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)
 Metaspace       used 2936K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNew: 6620K->972K(9216K), 0.0165806 secs] 6620K->5068K(19456K), 0.0167103 secs] [Times: user=0.00 sys=0.01, real=0.02 secs] 
Heap after GC invocations=1 (full 0):
 def new generation   total 9216K, used 972K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   0% used [0x00000000fec00000, 0x00000000fec00000, 0x00000000ff400000)
  from space 1024K,  95% used [0x00000000ff500000, 0x00000000ff5f33d0, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 2936K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=1 (full 0):
 def new generation   total 9216K, used 5068K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  50% used [0x00000000fec00000, 0x00000000ff000010, 0x00000000ff400000)
  from space 1024K,  95% used [0x00000000ff500000, 0x00000000ff5f33d0, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 2937K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
[GC (Allocation Failure) [DefNew: 5068K->0K(9216K), 0.0051671 secs] 9164K->4999K(19456K), 0.0052174 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
Heap after GC invocations=2 (full 0):
 def new generation   total 9216K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,   0% used [0x00000000fec00000, 0x00000000fec00000, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4999K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  48% used [0x00000000ff600000, 0x00000000ffae1e58, 0x00000000ffae2000, 0x0000000100000000)
 Metaspace       used 2937K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K
}
Disconnected from the target VM, address: '127.0.0.1:43367', transport: 'socket'
Heap
 def 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 4999K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  48% used [0x00000000ff600000, 0x00000000ffae1e58, 0x00000000ffae2000, 0x0000000100000000)
 Metaspace       used 2943K, capacity 4568K, committed 4864K, reserved 1056768K
  class space    used 315K, capacity 392K, committed 512K, reserved 1048576K

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值