Java虚拟机日志与参数

打印GC日志可以使用参数-XX:+PrintGC
/**
 * -Xmx10m -Xms10m -XX:PretenureSizeThreshold=10485760
 *  -XX:+PrintGC -XX:+UseSerialGC
 */
public class GCLogTest {

private static final int CAPACITY = 6*1024*1024;
   
    public static void main(String[] args) {
       
        byte[] a1 = new byte[CAPACITY];
        a1 = null;
        System.out.println("1111");
       
        byte[] a2 = new byte[CAPACITY];
        a2 = null;
        System.out.println("2222");

byte[] a3 = new byte[CAPACITY];
        System.out.println("3333");   
    } 
}

1111
[GC (Allocation Failure)  7004K->6676K(9920K), 0.0011969 secs]
[Full GC (Allocation Failure)  6676K->531K(9920K), 0.0014471 secs]
2222
[GC (Allocation Failure)  6730K->6675K(9920K), 0.0001418 secs]
[Full GC (Allocation Failure)  6675K->531K(9920K), 0.0010813 secs]
3333

如果需要更详细的信息,可以使用-XX:+PrintGCDetails
1111
[GC (Allocation Failure) [DefNew: 809K->320K(3072K), 0.0012200 secs][Tenured: 6353K->528K(6848K), 0.0012924 secs] 6953K->528K(9920K), [Metaspace: 2645K->2645K(1056768K)], 0.0025600 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2222
[GC (Allocation Failure) [DefNew: 55K->0K(3072K), 0.0002147 secs][Tenured: 6672K->528K(6848K), 0.0011609 secs] 6727K->528K(9920K), [Metaspace: 2645K->2645K(1056768K)], 0.0014129 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
3333

以上面的第一次GC进行分析,GC日志开头的“GC”,说明这次垃圾回收的停顿类型。一般还有Full GC (System.gc()) 和Full GC,前者说明是调用System.gc()触发,后者一般是分配担保失败的原因。而a1变量所占用内存是6M,结合GC日志新生代占用809k,老年代占用6353k,可以推断出是内存直接分配在老年代的。《深入理解Java虚拟机》中内存分配和回收策略中有“大对象直接进入老年代”。所谓的大对象是指需要大量连续内存空间的Java对象,最典型的大对象就是很长的字符串及数组。

接下来DefNew、Tenured、Metaspace(使用的是JDK1.8,在该版本之下是Perm)表示GC发生的区域,这里显示的区域名和具体使用的垃圾回收器密切相关。如DefNew(Default New Generation),说明使用时Serial收集器,刚好使用-XX:+UseSerialGC参数。如果是ParNew收集器,新生代名称是ParNew(Parallel New Generation),如果采用Parallel Scavenge收集器,它的新生代名称是PSYoungGen。老年代和永久代同理。

“809K->320K(3072K), 0.0012200 secs”表示“新生代GC前使用的内存->新生代GC后使用的内存(新生代的可使用总内存(eden+from内存),此次GC占用时间”。“6353K->528K(6848K)”表示“老年代GC前使用的内存->老年代GC后使用的内存(老年代的总内存)”。“6953K->528K(9920K)”表示“GC前堆使用的内存->GC后堆使用的内存(堆的总内存)”。3072加6848刚好等于9920。

2|0堆参数配置

堆内存是Java进程的重要组成部分,几乎所有与应用相关的内存空间都和堆有关。堆的相关参数可以说是Java虚拟机中最重要的,也对程序的性能有着重要的影响,而且内存溢出最常见的就是发生在堆中。

2|1最大堆和堆初始配置

当虚拟机启动,虚拟机就会初始化堆空间,可以使用-Xms来指定。一般来说虚拟机会尽可能维持在初始堆空间的范围内运行。但是如果初始堆空间耗尽,虚拟机会对空间进行扩展,其上限为最堆对空间,由-Xmx参数来指定。
    private static final int CAPACITY = 6*1024*1024;

/**
    * -Xms10m
    */
    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().totalMemory()+" bytes");
        byte[] a1 = new byte[CAPACITY];
        byte[] a2 = new byte[CAPACITY];
        System.out.println(Runtime.getRuntime().totalMemory()+" bytes");
       
    }

结果如下,说明了虚拟机对堆内存进行了扩展
9961472 bytes
16777216 bytes

在实际项目中,可以将-Xms和-Xmx设置相等,这样的好处是可以减少程序运行时进行的垃圾回收次数,从而提高性能

2|2新生代配置

参数-Xmn可以用于设置新生代的大小,那么老年代的大小就是-Xms减去-Xmn的值。新生代的值一般设置在整个堆空间的1/4到1/3左右。除了可以用-Xmn指定新生代的绝对大小,还可以使用-XX:NewRatio来设置新生代和老年代的比例。-XX:NewRatio=老年代/新生代

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/67cc13ab2c92655e261fb3167cd8ed65.html