7mxx:常用JVM配置参数

此博客为炼数成金JVM课程的第三课:常用JVM配置参数

目录

  • Trace跟踪参数
  • 堆的分配参数
  • 栈的分配参数
  • Trace跟踪参数

    -verbose:gc

    打开GC的跟踪日志

    -XX:+printGC

    可以打印GC的简要信息

    [GC 4790K->374K(15872K), 0.0001606 secs][GC 4790K->374K(15872K), 0.0001474 secs][GC 4790K->374K(15872K), 0.0001563 secs][GC 4790K->374K(15872K), 0.0001682 secs]

    在GC之后,从4790K变成了374K,整个堆的大小在15872K,花费了0.0001606 secs

    -XX:+ PrintGCDetails

    打印GC的详细信息
    -XX:+PrintGTimeStamps
    打印GC发生的时间戳

    例子:

    [GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 新生代GC: 4416K 变成了 0K,总大小是4928K原生代GC: 4790K 变成了 374K

    -XX:+PrintGCDetails的输出

    Heap def new generation total 13824K, used 11223K [0x27e80000, 0x28d80000, 0x28d80000) 新生代一共有13824K可以用,已经用了11223K eden space 12288K, 91% used [0x27e80000, 0x28975f20, 0x28a80000) eden(对象出生的地方) 可用的空间是12288K,已经使用了91% from space 1536K, 0% used [0x28a80000, 0x28a80000, 0x28c00000) to space 1536K, 0% used [0x28c00000, 0x28c00000, 0x28d80000) from 和 to 幸存带,大小一定是相等的 tenured generation total 5120K, used 0K [0x28d80000, 0x29280000, 0x34680000) 老年代的GC,一共是5120K,没有被使用 the space 5120K, 0% used [0x28d80000, 0x28d80000, 0x28d80200, 0x29280000) compacting perm gen total 12288K, used 142K [0x34680000, 0x35280000, 0x38680000) 永久区方法区,总共是12288K,已经使用了142K,使用的比较少,因为在JDK5之后,在串行GC的模式下会有类的共享,一些基础java类会被加载到一个共享区间 the space 12288K, 1% used [0x34680000, 0x346a3a90, 0x346a3c00, 0x35280000) ro space 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000) rw space 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000 ro 和 rw 是共享空间,一个只读,一个可读可写

    在程序结束后,PrintGCDetails 会把整个程序的堆的基本状况打印出来。

    [0x27e80000, 0x28d80000, 0x28d80000)
    低边界 当前边界 最该边界

    (0x28d80000 - 0x27e80000) / 1024 /1024 = 15M
    可见给新生代GC分配了15M的内容
    eden space + from space + to space = 15M

    而新生代可使用的内存只有13824K,并不足15M,这跟GC算法有关,后续文章会讲到

    -Xloggc: log/gc.log

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

    -XX:+PrintHeapAtGc

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

    {Heap before GC invocations=0 (full 0): def new generation total 3072K, used 2752K [0x33c80000, 0x33fd0000, 0x33fd0000) eden space 2752K, 100% used [0x33c80000, 0x33f30000, 0x33f30000) from space 320K, 0% used [0x33f30000, 0x33f30000, 0x33f80000) to space 320K, 0% used [0x33f80000, 0x33f80000, 0x33fd0000) tenured generation total 6848K, used 0K [0x33fd0000, 0x34680000, 0x34680000) the space 6848K, 0% used [0x33fd0000, 0x33fd0000, 0x33fd0200, 0x34680000) compacting perm gen total 12288K, used 143K [0x34680000, 0x35280000, 0x38680000) the space 12288K, 1% used [0x34680000, 0x346a3c58, 0x346a3e00, 0x35280000) ro space 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000) rw space 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)**[GC[DefNew: 2752K->320K(3072K), 0.0014296 secs] 2752K->377K(9920K), 0.0014604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]** 发生了一次GCHeap after GC invocations=1 (full 0): def new generation total 3072K, used 320K [0x33c80000, 0x33fd0000, 0x33fd0000) eden space 2752K, 0% used [0x33c80000, 0x33c80000, 0x33f30000) from space 320K, 100% used [0x33f80000, 0x33fd0000, 0x33fd0000) to space 320K, 0% used [0x33f30000, 0x33f30000, 0x33f80000) tenured generation total 6848K, used 57K [0x33fd0000, 0x34680000, 0x34680000) the space 6848K, 0% used [0x33fd0000, 0x33fde458, 0x33fde600, 0x34680000) compacting perm gen total 12288K, used 143K [0x34680000, 0x35280000, 0x38680000) the space 12288K, 1% used [0x34680000, 0x346a3c58, 0x346a3e00, 0x35280000) ro space 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000) rw space 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)}

    -XX:+TraceClassLoading

    监控系统中每一个类的加载

    [Loaded java.lang.Object from shared objects file][Loaded java.io.Serializable from shared objects file][Loaded java.lang.Comparable from shared objects file][Loaded java.lang.CharSequence from shared objects file][Loaded java.lang.String from shared objects file][Loaded java.lang.reflect.GenericDeclaration from shared objects file][Loaded java.lang.reflect.Type from shared objects file]

    在做一些跟踪调试的时候可以加上,可以看到那些类被加载进来

    -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

    分别显示:序号、实例数量、总大小、类型
    可见,这个程序中,byte,HashMap,Long 使用的特别多。可通过这个方法去判断哪种类型使用的较多,针对性的处理

    堆的分配参数

    -Xmx -Xms

    指定最大堆(最大使用的空间)和最小堆(最小使用的空间)

    -Xmx20m -Xms5m 运行以下代码

    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.0Mfree mem=4.617515563964844Mtotal mem=5.5M byte[] b=new byte[1*1024*1024];System.out.println("分配了1M空间给数组");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");

    输出结果

    分配了1M空间给数组Xmx=18.0Mfree mem=3.6175994873046875Mtotal mem=5.5M

    可见,free mem 少了1M,因为分配给了byte数组,但总空间是不变的
    在Java中,java会尽可能维持在最小堆,我们限制了5M,所以java会尽量在5M的空间内运行,如果无法满足要求才会扩容

    b=new byte[4*1024*1024];System.out.println("分配了4M空间给数组");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");

    输出结果

    分配了4M空间给数组Xmx=18.0Mfree mem=5.1175994873046875Mtotal mem=10.0M

    可见,total mem变多了,但是 Xmx 最大的空间还是没有变

    System.gc();System.out.println("回收内存");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.0Mfree mem=4.9048309326171875Mtotal mem=5.5M

    可见,做了GC释放之后,free mem 增多了

    -Xmn

    设置新生代大小(设置的是绝对值)

    -XX:NewRatio

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

    -XX:SurvivoRation

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

    public static void main(String[] args) { byte[] b=null; for(int i=0;i<10;i++) b=new byte[1*1024*1024];} 运行参数: -Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails

    输出结果
    这里写图片描述

    可见,新生代被分配了1M,没有GC发生。新生代比较小,我们需要10M的空间,所以全部被分配在老年代。

    运行参数: -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails

    输出结果
    这里写图片描述
    可见,仍然没有触发GC,当新生代的大小为15M,满足我们需求的10M要求,所以都被分配在新生代,老年代没有用到。

    运行参数: -Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails

    输出结果
    这里写图片描述

    可见触发了两次新生代GC,但是因为新生代的from 和 to 的空间太小,所以部分内容放到了老年代

    运行参数: -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

    输出结果
    这里写图片描述

    可见,触发了三次新生代的GC,并且新生代的from和to 增大了,新生代自己可以处理这些byte,所以并没有使用到老年代

    运行参数: -Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:+PrintGCDetails

    输出结果
    这里写图片描述
    一共进行了两次新生代GC, 没有老年代GC发生

    运行参数:-Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=3 -XX:+PrintGCDetails

    输出结果
    这里写图片描述

    GC越多,一般情况下对系统越不好,幸存带大了之后,对空间的浪费是很严重的,在此处合理的减小了幸存带的大小,eden区就可以扩大到6M
    在做了该设置后可见新生代GC少了一次,新生代自己就可以完成工作,无需用到老年代

    -XX: +HeapDumpOnOutOfMemoryError

    OOM时导出堆到文件

    -XX: +HeadDumpHead

    导出OOM的路径

    例子:

    -Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump Vector v=new Vector(); for(int i=0;i<25;i++) v.add(new byte[1*1024*1024]); java.lang.OutOfMemoryError: Java heap spaceDumping heap to a.dump ...Heap dump file created [15936364 bytes in 0.015 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.tianyi.test.TestXmxXms.main(TestXmxXms.java:11)

    -XX:OnOutOfMemoryError

    在OOM的时候,执行一个脚本
    “-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p“
    当程序OOM时,在D:/a.txt中将会生成线程的dump
    可以在OOM时,发送邮件,甚至是重启程序

    堆的分配参数总结:

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

    永久区分配参数

    -XX:PermSize -XX:MaxPermSize

    设置永久区的初始空间和最大空间
    他们表示,一个系统可以容纳多少个类型
    一般几十M几百M就足够了

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

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

    这里写图片描述
    可见,新生代和老年代都没有占用太大的空间,但是永久区占用了99%,导致了OOM

    打开上述程序生成的Dump
    堆空间实际占用非常少
    但是永久区溢出,一样抛出OOM
    这里写图片描述
    所以,如果堆空间没有用完也抛出了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(); } }} 递归调用-Xss128Kdeep of calling = 302java.lang.StackOverflowError at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:9) at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:11) at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:11) at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:11) at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:11) ...-Xss256Kdeep of calling = 1127java.lang.StackOverflowError at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:11) at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:11) at com.tianyi.test.TestXmxXms.recursion(TestXmxXms.java:11) ......

    可见,Xss 越大,函数调用的次数可以增加

    让函数尽可能多调用的方式:

    第一种: 增大Xss
    第二种:尽量减少局部变量的数量,可以减少每一个弹出调用栈帧的空间,可以让函数多调用几次。

    相关推荐

    相关文章