java jvm 参数配置_Java虚拟机详解----常用JVM配置参数

[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后,打印类的信息。

例如:

62eb4c179caebe9958c44f28f6a1c46e.png

二、堆的分配参数:

1、-Xmx –Xms:指定最大堆和最小堆

举例、当参数设置为如下时:

-Xmx20m -Xms5m

然后我们在程序中运行如下代码:

System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间

System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间

System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间

运行效果:

575b0518d54329db893fc4a6838f37c6.png

保持参数不变,在程序中运行如下代码:(分配1M空间给数组)

48304ba5e6f9fe08f3fa1abda7d326ab.png

byte[] b = new byte[1 * 1024 * 1024];

System.out.println("分配了1M空间给数组");

System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间

System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间

System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行效果:

65f043fee210ba7c97402d8a284fe949.png

注:Java会尽可能将total mem的值维持在最小堆。

保持参数不变,在程序中运行如下代码:(分配10M空间给数组)

48304ba5e6f9fe08f3fa1abda7d326ab.png

byte[] b = new byte[10 * 1024 * 1024];

System.out.println("分配了10M空间给数组");

System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间

System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间

System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行效果:

51eaeea29ffeba09a04d3eb1095f8e65.png

如上图红框所示:此时,total mem 为7M时已经不能满足需求了,于是total mem涨成了16.5M。

保持参数不变,在程序中运行如下代码:(进行一次GC的回收)

48304ba5e6f9fe08f3fa1abda7d326ab.png

System.gc();

System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间

System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间

System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间

48304ba5e6f9fe08f3fa1abda7d326ab.png

运行效果:

c7a05b9e54dbc441de02d10ca10eebec.png

问题1: -Xmx(最大堆空间)和 –Xms(最小堆空间)应该保持一个什么关系,可以让系统的性能尽可能的好呢?

问题2:如果你要做一个Java的桌面产品,需要绑定JRE,但是JRE又很大,你如何做一下JRE的瘦身呢?

2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio:

-Xmn

设置新生代大小

-XX:NewRatio

新生代(eden+2*s)和老年代(不包含永久区)的比值

例如:4,表示新生代:老年代=1:4,即新生代占整个堆的1/5

-XX:SurvivorRatio(幸存代)

设置两个Survivor区和eden的比值

例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10

现在运行如下这段代码:

48304ba5e6f9fe08f3fa1abda7d326ab.png

public class JavaTest {

public static void main(String[] args) {

byte[] b = null;

for (int i = 0; i < 10; i++)

b = new byte[1 * 1024 * 1024];

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

我们通过设置不同的jvm参数,来看一下GC日志的区别。

(1)当参数设置为如下时:(设置新生代为1M,很小)

-Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails

运行效果:

0f5c69ae9c3b834615d0f2d77cd43031.png

总结:

没有触发GC

由于新生代的内存比较小,所以全部分配在老年代。

(2)当参数设置为如下时:(设置新生代为15M,足够大)

-Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails

运行效果:

523068156f08122380e840310806119e.png

上图显示:

没有触发GC

全部分配在eden(蓝框所示)

老年代没有使用(红框所示)

(3)当参数设置为如下时:(设置新生代为7M,不大不小)

-Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails

运行效果:

874d532e5346df140046144d458128f6.png

总结:

进行了2次新生代GC

s0 s1 太小,需要老年代担保

(4)当参数设置为如下时:(设置新生代为7M,不大不小;同时,增加幸存代大小)

-Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

运行效果:

673e54c92578187aec55a2b8a691dbfb.png

总结:

进行了至少3次新生代GC

s0 s1 增大

(5)当参数设置为如下时:

-Xmx20m -Xms20m -XX:NewRatio=1

-XX:SurvivorRatio=2 -XX:+PrintGCDetails

运行效果:

76f0ae8b823f1d669e86707fb22426fb.png

(6)当参数设置为如下时: 和上面的(5)相比,适当减小幸存代大小,这样的话,能够减少GC的次数

-Xmx20m -Xms20m -XX:NewRatio=1

-XX:SurvivorRatio=3 -XX:+PrintGCDetails

34a891961a2ec61c21c7b6db3ce10718.png

3、-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath

-XX:+HeapDumpOnOutOfMemoryError

OOM时导出堆到文件

根据这个文件,我们可以看到系统dump时发生了什么。

-XX:+HeapDumpPath

导出OOM的路径

例如我们设置如下的参数:

-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump

上方意思是说,现在给堆内存最多分配20M的空间。如果发生了OOM异常,那就把dump信息导出到d:/a.dump文件中。

然后,我们执行如下代码:

Vector v = new Vector();

for (int i = 0; i < 25; i++)

v.add(new byte[1 * 1024 * 1024]);

上方代码中,需要利用25M的空间,很显然会发生OOM异常。现在我们运行程序,控制台打印如下:

0bf016fb52280ed4290763c69bb7278a.png

现在我们去D盘看一下dump文件:

27e1493c1cb87379b2c1ac372687b88b.png

上图显示,一般来说,这个文件的大小和最大堆的大小保持一致。

我们可以用VisualVM打开这个dump文件。

注:关于VisualVM的使用,可以参考下面这篇博客:

或者使用Java自带的JavaVisualVM工具也行:

96312152ba0121156e991d15fe2ccc49.png

1b14fa7930548b011692932c34145ffb.png

上图中就是dump出来的文件,文件中可以看到,一共有19个byte已经被分配了。

4、-XX:OnOutOfMemoryError:

-XX:OnOutOfMemoryError

在OOM时,执行一个脚本。

可以在OOM时,发送邮件,甚至是重启程序。

例如我们设置如下的参数:

-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p //p代表的是当前进程的pid

上方参数的意思是说,执行printstack.bat脚本,而这个脚本做的事情是:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt,即当程序OOM时,在D:/a.txt中将会生成线程的dump。

5、堆的分配参数总结:

根据实际事情调整新生代和幸存代的大小

官方推荐新生代占堆的3/8

幸存代占新生代的1/10

在OOM时,记得Dump出堆,确保可以排查现场问题

6、永久区分配参数:

-XX:PermSize  -XX:MaxPermSize

设置永久区的初始空间和最大空间。也就是说,jvm启动时,永久区一开始就占用了PermSize大小的空间,如果空间还不够,可以继续扩展,但是不能超过MaxPermSize,否则会OOM。

他们表示,一个系统可以容纳多少个类型

代码举例:

我们知道,使用CGLIB等库的时候,可能会产生大量的类,这些类,有可能撑爆永久区导致OOM。于是,我们运行下面这段代码:

for(int i=0;i<100000;i++){

CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());

}

上面这段代码会在永久区不断地产生新的类。于是,运行效果如下:

a57a585108a1784af06851ba32128b07.png

总结:

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

堆空间实际占用非常少,但是永久区溢出 一样抛出OOM。

三、栈的分配参数:

1、Xss:

设置栈空间的大小。通常只有几百K

决定了函数调用的深度

每个线程都有独立的栈空间

局部变量、参数 分配在栈上

注:栈空间是每个线程私有的区域。栈里面的主要内容是栈帧,而栈帧存放的是局部变量表,局部变量表的内容是:局部变量、参数。

我们来看下面这段代码:(没有出口的递归调用)

48304ba5e6f9fe08f3fa1abda7d326ab.png

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();

}

}

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

上方这段代码是没有出口的递归调用,肯定会出现OOM的。

如果设置栈大小为128k:

-Xss128K

运行效果如下:(方法被调用了294次)

9edc6ba0582b78f405ffc5d72c5fd258.png

如果设置栈大小为256k:(方法被调用748次)

3214edc297d81d46561e904f0cae8030.png

意味着函数调用的次数太深,例如递归调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值