常用JVM配置参数

本文主要探讨JVM的配置参数,包括Trace跟踪参数如verbose:gc、PrintGCDetails,堆分配参数如Xmx、Xms,以及永久区和栈大小的设置。通过示例解释了不同参数如何影响JVM的行为,帮助优化Java应用程序的性能和内存管理。
摘要由CSDN通过智能技术生成

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

目录

  1. Trace跟踪参数
  2. 堆的分配参数
  3. 栈的分配参数

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]** 
发生了一次GC
Heap 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.0M
free mem=4.617515563964844M
total 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.0M
free mem=3.6175994873046875M
total 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.0M
free mem=5.1175994873046875M
total 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.0M
free mem=4.9048309326171875M
total 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 space
Dumping 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();
        }
    }
}
递归调用
-Xss128K
deep of calling = 302
java.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)
    ...
-Xss256K
deep of calling = 1127
java.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
第二种:尽量减少局部变量的数量,可以减少每一个弹出调用栈帧的空间,可以让函数多调用几次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值