zoukankan      html  css  js  c++  java
  • 深入JVM内核(二)常用的JVM配置参数

    date: 2018-10-14 13:02:33

    前言

    目的:监控GC执行和JVM基本内存状态。

    Trace跟踪参数

    -verbose:gc or -XX:+PrintGC

    打印GC的简要信息
    在虚拟机发生内存回收时在输出设备显示信息

    实例:

    /**
     * @author ZenoYang
     * with VM args: -verbose:gc -XX:+PrintGC
     */
    public class TraceTest {
        public static void main(String[] args) {
            Object obj = new byte[1024*1024]; // 申请1M的空间
            obj = null; // 把引用obj置为null好让GC回收刚刚申请的内存空间
            System.gc();    // 调用GC
        }
    }
    

    输出为:

    [GC (System.gc())  4355K->880K(125952K), 0.0018415 secs]
    [Full GC (System.gc())  880K->770K(125952K), 0.0083402 secs]
    

    日志分析:
    GC也叫Minor GC(年轻代GC),专门回收年轻代区域的内存。Full GC也叫Major GC(老年代GC),专门回收老年代区域的内存。4355K->880K表示GC回收了(4355-880)k的内存,两个125952K表示整个年轻代和年老代区域各占有125952K的内存,0.0018415 secs代表回收所花费的时间。

    -XX:+PrintGCDetails

    用于打印GC详细信息

    还是刚才的例子(-XX:+PrintGCDetails),输出为:

    [GC (System.gc()) [PSYoungGen: 4355K->856K(38400K)] 4355K->864K(125952K), 0.0468308 secs] [Times: user=0.02 sys=0.00, real=0.05 secs] 
    [Full GC (System.gc()) [PSYoungGen: 856K->0K(38400K)] [ParOldGen: 8K->781K(87552K)] 864K->781K(125952K), [Metaspace: 3336K->3336K(1056768K)], 0.0223777 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
    Heap
     PSYoungGen      total 38400K, used 333K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
      eden space 33280K, 1% used [0x00000000d5e00000,0x00000000d5e534a8,0x00000000d7e80000)
      from space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
      to   space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
     ParOldGen       total 87552K, used 781K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
      object space 87552K, 0% used [0x0000000081a00000,0x0000000081ac3500,0x0000000086f80000)
     Metaspace       used 3342K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 361K, capacity 388K, committed 512K, reserved 1048576K
    

    日志分析:
    eden表示新生代,form和to表示两个幸存代(Survivor)区域,PSPermGen表示持久代区域。
    [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)分别代表低边界(起始的内存地址)、当前边界(当前占用到哪个位置)、最高边界(该区域最大能申请到的位置)
    其它内容:emmm TODO

    -XX:+PrintHeapAtGC

    每次一次GC后,都打印堆信息

    还是刚才的例子(-XX:+PrintHeapAtGC),输出为:

    {Heap before GC invocations=1 (full 0):
     PSYoungGen      total 38400K, used 4355K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
      eden space 33280K, 13% used [0x00000000d5e00000,0x00000000d6240dc0,0x00000000d7e80000)
      from space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
      to   space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
     ParOldGen       total 87552K, used 0K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
      object space 87552K, 0% used [0x0000000081a00000,0x0000000081a00000,0x0000000086f80000)
     Metaspace       used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 342K, capacity 388K, committed 512K, reserved 1048576K
    Heap after GC invocations=1 (full 0):
     PSYoungGen      total 38400K, used 840K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
      eden space 33280K, 0% used [0x00000000d5e00000,0x00000000d5e00000,0x00000000d7e80000)
      from space 5120K, 16% used [0x00000000d7e80000,0x00000000d7f52020,0x00000000d8380000)
      to   space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
     ParOldGen       total 87552K, used 8K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
      object space 87552K, 0% used [0x0000000081a00000,0x0000000081a02000,0x0000000086f80000)
     Metaspace       used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 342K, capacity 388K, committed 512K, reserved 1048576K
    }
    {Heap before GC invocations=2 (full 1):
     PSYoungGen      total 38400K, used 840K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
      eden space 33280K, 0% used [0x00000000d5e00000,0x00000000d5e00000,0x00000000d7e80000)
      from space 5120K, 16% used [0x00000000d7e80000,0x00000000d7f52020,0x00000000d8380000)
      to   space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
     ParOldGen       total 87552K, used 8K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
      object space 87552K, 0% used [0x0000000081a00000,0x0000000081a02000,0x0000000086f80000)
     Metaspace       used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 342K, capacity 388K, committed 512K, reserved 1048576K
    Heap after GC invocations=2 (full 1):
     PSYoungGen      total 38400K, used 0K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
      eden space 33280K, 0% used [0x00000000d5e00000,0x00000000d5e00000,0x00000000d7e80000)
      from space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
      to   space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
     ParOldGen       total 87552K, used 744K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
      object space 87552K, 0% used [0x0000000081a00000,0x0000000081aba178,0x0000000086f80000)
     Metaspace       used 3153K, capacity 4496K, committed 4864K, reserved 1056768K
      class space    used 342K, capacity 388K, committed 512K, reserved 1048576K
    }
    

    日志分析:TODO

    -XX:+TraceClassLoading

    监控类的加载过程

    还是刚才的例子(-XX:+TraceClassLoading),输出为:

    [Opened D:Program FilesJavajdk1.8.0_51jrelib
    t.jar]
    [Loaded java.lang.Object from D:Program FilesJavajdk1.8.0_51jrelib
    t.jar]
    [Loaded java.io.Serializable from D:Program FilesJavajdk1.8.0_51jrelib
    t.jar]
    [Loaded java.lang.Comparable from D:Program FilesJavajdk1.8.0_51jrelib
    t.jar]
    [Loaded java.lang.CharSequence from D:Program FilesJavajdk1.8.0_51jrelib
    t.jar]
    [Loaded java.lang.String from D:Program FilesJavajdk1.8.0_51jrelib
    t.jar]
    [Loaded java.lang.reflect.AnnotatedElement from D:Program FilesJavajdk1.8.0_51jrelib
    t.jar]
    ...(还有很多)
    

    -XX:+PrintClassHistogram

    打印类的信息

    程序运行过程中,在控制台下按下Ctrl+Break时打印信息(不是上面的例子打印出来的信息):

     num     #instances         #bytes  class name
    ----------------------------------------------
       1:        890617      470266000  [B
       2:        890643       21375432  java.util.HashMap$Node
       3:        890608       14249728  java.lang.Long
       4:            13        8389712  [Ljava.util.HashMap$Node;
       5:          2062         371680  [C
       6:           463          41904  java.lang.Class
    

    分别显示:序号、实例数量、总大小、类型

    -Xloggc:log/gc.log

    指定GC log的位置,以文件输出
    重定向GC log,帮助开发人员分析问题

    还是刚才的例子(-Xloggc:log/gc.log -XX:+PrintGCDetails),log为文件夹,需要提前创建好,gc.log为产生的日志文件

    堆的分配参数

    -Xmx –Xms

    指定最大堆和最小堆

    实例,设置最大堆为20M,最小堆为5M:

    /**
     * with VM args: -Xmx20m -Xms5m
     */
    public class HeapTest {
        public static void main(String[] args) {
            System.out.print("Xmx=");
            System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M");
    
            System.out.print("free mem=");
            System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M");
    
            System.out.print("total mem=");
            System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M");
        }
    }
    

    输出为:

    Xmx=18.0M
    free mem=4.041107177734375M
    total mem=5.5M
    

    若分配1M空间给数组,在main方法最前面添加下面代码:

    byte[] b=new byte[1*1024*1024];
    System.out.println("分配了1M空间给数组");
    

    输出为:

    分配了1M空间给数组
    Xmx=18.0M
    free mem=3.017578125M
    total mem=5.5M
    

    这时候会发现剩余内存少了1M

    若分配4M空间给数组:

    byte[] b = new byte[4*1024*1024];
    System.out.println("分配了4M空间给数组");
    

    输出为:

    分配了4M空间给数组
    Xmx=18.0M
    free mem=4.4584503173828125M
    total mem=10.0M
    

    这时候会发现,总内存变大了。这里做了堆的扩容。

    若分配4M空间,再使用GC回收:

    byte[] b = new byte[4*1024*1024];
    System.out.println("分配了4M空间给数组");
    System.gc();
    

    输出为:

    分配了4M空间给数组
    Xmx=18.0M
    free mem=6.177040100097656M
    total mem=11.0M
    

    会发现,剩余内存有增加了。

    -Xmn

    设置新生代大小(新生代包括一个eden区和两个Survivor幸存区)

    实例:

    VM args: -Xmx20m -Xms20m –Xmn7m  -XX:+PrintGCDetails 
    

    -XX:NewRatio

    新生代(eden+2*s)和老年代(不包含永久区)的比值
    4 表示 新生代:老年代=1:4,即年轻代占堆的1/5

    -XX:SurvivorRatio

    设置两个Survivor区和eden的比
    8表示 两个Survivor :eden=2:8,即一个Survivor占年轻代的1/10

    实例:

    VM args: -Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=3 -XX:+PrintGCDetails
    

    -XX:+HeapDumpOnOutOfMemoryError-XX:+HeapDumpPath

    -XX:+HeapDumpOnOutOfMemoryError: OOM时导出堆到文件
    -XX:+HeapDumpPath: 导出OOM的路径

    实例:

    /**
     * with VM args: -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=e:/a.dump
     */
    public class HeapTest {
        public static void main(String[] args) {
            Vector v = new Vector();
            for(int i = 0; i < 25; i++) {
                v.add(new byte[1024*1024]);
            }
        }
    }
    

    输出:

    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to e:/a.dump ...
    Heap dump file created [16423219 bytes in 0.101 secs]
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at HeapTest.main(HeapTest.java:10)
    

    此时在E盘下产生了dump文件

    -XX:OnOutOfMemoryError

    在OOM时,执行一个脚本

    实例:

    VM args: -XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p
    
    printstack.bat:
    D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt
    

    当程序OOM时,在D:/a.txt中将会生成线程的dump
    通过这个参数,可以在OOM时,发送邮件,或者重启程序等

    堆分配的总结

    • 根据实际情况调整新生代和幸存代的大小
    • 官方推荐新生代占堆的3/8
    • 幸存代占新生代的1/10
    • 在OOM时,记得Dump出堆,确保可以排查现场问题

    永久区参数分配

    -XX:PermSize -XX:MaxPermSize

    设置永久区的初始空间和最大空间
    他们表示,一个系统可以容纳多少个类型

    使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM

    for(int i = 0; i < 100000; i++) {
        CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
    }
    

    所以,如果堆空间没有用完也抛出了OOM,有可能是永久区导致的

    栈的大小分配

    -Xss

    通常只有几百K
    决定了函数调用的深度
    每个线程都有独立的栈空间
    局部变量、参数 分配在栈上

    实例:

    public class TestStackDeep {
        private static int count = 0;
        public static void recursion(long a, long b, long c) {
            long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10;
            count++;
            recursion(a, b, c);
        }
        public static void main(String args[]){
            try{
                recursion(0L,0L,0L);
            }catch(Throwable e){
                System.out.println("deep of calling = "+count);
                e.printStackTrace();
            }
        }
    }
    

    当VM参数为-Xss128K时:

    java.lang.StackOverflowError
    deep of calling = 425
    ...
    

    当VM参数为-Xss256K时:

    deep of calling = 2051
    java.lang.StackOverflowError
    ...
    

    参考:

  • 相关阅读:
    使用正则表达式,取得点击次数,函数抽离
    爬取校园新闻首页的新闻
    网络爬虫基础练习
    综合练习:词频统计
    【Art】虹吸原理(5月份订正版)
    【失踪人口】准初三的JZSC
    【Mood】出大问题(最近很喜欢说这句话)
    【Matrix】矩阵加法&乘法
    【C++】stdio.h和cstdio
    【Theorem】中国剩余定理
  • 原文地址:https://www.cnblogs.com/cloudflow/p/13894272.html
Copyright © 2011-2022 走看看