鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 编程语言开发 > erlang > >

JVM 常见参数设置和分析

来源:互联网 作者:佚名 时间:2016-07-17 21:14
打印 GC 日志 这里以 IDEA 为例: 菜单栏: Run - Edit Configurations - VM options 添加 -XX:+PrintGCDetail 写个简单的测试程序 package VM; /** * Created by andy.wwh on 2016/7/16. */ public class TestPrintGcDetails { public static void main (St

打印 GC 日志

这里以 IDEA 为例:

菜单栏:

Run -> Edit Configurations -> VM options 添加 -XX:+PrintGCDetail

GC 参数设置

写个简单的测试程序

package VM;

/**
 * Created by andy.wwh on 2016/7/16.
 */
public class TestPrintGcDetails {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            byte[] b = new byte[1*1024*1024];
        }
    }
}

上图中除了各个区的内存分配情况,还发生了一次 GC。

GC日志:[GC (Allocation Failure) [PSYoungGen: 1024K->488K(1536K)] 1024K->648K(19968K), 0.0007228 secs] [Times: user=0.05 sys=0.00, real=0.00 secs]

从这条日志我们可以看出新生代 YoungGen 分配失败,1024K->488K(1536) 表示 GC 前已使用->GC后已使用(该区域总容量),[]之外表示Java堆GC前已使用->GC后已使用(Java堆总容量),最后为 GC 耗时。

从上图我们可以看出 GC 的主要收集区域,包括 PSYoungGen(年轻代)、ParOldGen(老年代)、Metaspace(元数据区)。

Java Heap

上图中却没有 Metaspace,而多了 Permanent Generation,原因 JDK 1.8 中用 Metaspace 取代了 Permanent。


-XX:PrintHeapAtGC 打印 GC 前后的堆分配信息

GC 前后

从上图我们可以看到添加了 -XX:PrintHeapAtGC 参数后可以清楚的看到堆分配信息,而且还可以看到 GC 数据的移动 PSYoungGen.eden space -> PSYoungGen.from space -> ParOldGen.object space。


-XX:+TraceClassLoading

类加载信息


堆分配参数

测试代码:

public class TestMaxMinHeap {
    public static void main(String[] args) {
        System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/1024/1024) + "M");
        System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/1024/1024) + "M");
        System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/1024/1024) + "M");
    }
}

未修改参数

无参


-Xmx 和 -Xms

最大堆-Xmx20m 设置 maxMemory 为 20 M
分配空间不能超过最大堆内存大小,否则会抛出 OutofMemory 异常

Xmx20

初始化堆-Xms20m 设置 total Memory 为 20 M
totalMemory 就是初始化堆大小,它的意思是一开始限定堆大小为多少,如果不够则可以扩充,但必须小于最大堆大小。

Xms20

关系如下:

关系


我们可以做个简单的小测试,最小堆设置为 5 M,最大堆设置为 20 M,分配内存来看看它们数值的变化。

/**
 * VM options:-Xmx20m -Xms5m
 */
public class TestMaxMinHeap {
    private static final int M = 1024 * 1024;
    public static void main(String[] args) {
        System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
        System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
        System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
    }
}

无分配

上图可以看出最大堆是 18 M,初始堆是 5 M,空闲堆是 4 M。

我们分配 2 M 来看看。

/**
 * VM options:-Xmx20m -Xms5m
 */
public class TestMaxMinHeap {
    private static final int M = 1024 * 1024;
    public static void main(String[] args) {
        byte b[] = new byte[2 * M];
        System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
        System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
        System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
    }
}

分配 2 M

最大和初始堆大小都不变,空闲数变为 2 M,耗费 2 M。

分配 6 M 来看看。

/**
 * VM options:-Xmx20m -Xms5m
 */
public class TestMaxMinHeap {
    private static final int M = 1024 * 1024;
    public static void main(String[] args) {
        byte b[] = new byte[6 * M];
        System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
        System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
        System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
    }
}

分配 6 M

可以看到,初始堆 大小从 5 M 变为了 12 M。说明初始堆是可以动态调整的。

分配大于最大堆试试。

/**
 * VM options:-Xmx20m -Xms5m
 */
public class TestMaxMinHeap {
    private static final int M = 1024 * 1024;
    public static void main(String[] args) {
        byte b[] = new byte[21 * M];
        System.out.println("maxMemory:" + ( Runtime.getRuntime().maxMemory()/M) + "M");
        System.out.println("freeMemory:" + (Runtime.getRuntime().freeMemory()/M) + "M");
        System.out.println("totalMemory:" + (Runtime.getRuntime().totalMemory()/M) + "M");
    }
}

溢出

上图可以看出,超过最大堆时就会出现溢出问题了。

我们可以根据自己应用的时机情况来动态调整该参数,提高性能,一般在高并发请款下,建议 -Xms 和 -Xmx 值相同,避免内存 收缩/增大 出现的性能问题。


-Xmn、-newRatio、-SurvivorRatio:

  • -Xmn:设置 新生代 大小
  • -NewRatio:设置 新生代老年代比率
  • -SurvivorRatio:设置新生代中 Eden spaceSurvivor space 的大小

我们来看测试它们的作用:

public class TestNewSpace {
    private static final int M = 1024*1024;
    public static void main(String[] args) {

        byte[] b = null;

        for (int i = 0; i < 10; ++i) {
            b = new byte[1*M];
        }
    }
}

参数:-Xmn1m -Xms20m -Xmx20m -XX:+PrintGCDetails
新生代大小设置为 1 M

结论:出发了两次 GC。当新生代太小时,会频繁触发 GC


参数:-Xmn15m -Xms20m -Xms20m -XX:PrintGCDetails
新生代大小设置为 15 M

结果:没有触发 GC,当 新生代 比较大时,对象全部分配到 新生代


参数:-Xmn7m -Xms20m -Xms20m -XX:PrintGCDetails
新生代设置不大不小

结果:触发两次 GC


参数:-Xmn7m -Xms20m -Xms20m -XX:PrintGCDetails -XX:SurvivorRatio=2
将新生代中的幸存代设大

结果:触发了三次 GC

官方推荐

  • 根据实际情况调整新生代和幸存代的大小
  • 官方推荐新生代占堆的 3/8
  • 幸存代占新生代的 1/10

错误时参数

  • -XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath: 根据 OOM 错误导出 dump 文件,指出路径
  • -XX:OnOutOfMemoryError:XXX.py 发生 OOM 时执行某个脚本

栈分配参数

-Xss

指定 线程栈 大小,如:-Xss128k,一般来说,如果程序有大规模的递归行为,可以设置到 512K-1M。

public class TestXssStack {
    public static int count = 0;

    public static void func() {
        count++;
        func();
    }

    public static void main(String[] args) {
        try {
            func();
        }catch (Throwable e){
            System.out.println("count:" + count);
        }
    }
}

没调整参数时,调用了 30343 次。
设置 -Xss2m

调整参数后调用了 92111 次。

网友评论
<