1.java虚拟机参数
在虚拟机运行的过程中,如果可以跟踪系统的运行状态,那么对于问题的故障排查会有一定的帮助,为此,虚拟机提供了一些跟踪系统状态的参数,使用给定的参数执行java虚拟机,就可以在系统运行时打印相关日志,用于分析实际问题。我们进行虚拟机参数配置,其实主要就是围绕着堆、栈、方法区进行配置。
2.堆分配参数
1)
-XX:+PrintGC 使用这个参数,虚拟机启动后,只要遇到GC就会打印日志。
-XX:+UseSerialGC 配置串行回收器
-XX:+PrintGCDetails 可以查看详细信息,包括各个区的情况
-Xms:设置java程序启动时初始堆大小
-Xmx:设置java程序能获得的最大堆大小
-Xmx20m -Xms5m -XX:+PrintCommandLineFlags : 可以将隐式或者显示传给虚拟机的参数输出
测试代码如下:
public class Test01 {
public static void main(String[] args) {
//查看GC信息
System.out.println("max memory:" + Runtime.getRuntime().maxMemory());
System.out.println("free memory:" + Runtime.getRuntime().freeMemory());
System.out.println("total memory:" + Runtime.getRuntime().totalMemory());
byte[] b1 = new byte[1*1024*1024];
System.out.println("分配了1M");
System.out.println("max memory:" + Runtime.getRuntime().maxMemory());
System.out.println("free memory:" + Runtime.getRuntime().freeMemory());
System.out.println("total memory:" + Runtime.getRuntime().totalMemory());
byte[] b2 = new byte[4*1024*1024];
System.out.println("分配了4M");
System.out.println("max memory:" + Runtime.getRuntime().maxMemory());
System.out.println("free memory:" + Runtime.getRuntime().freeMemory());
System.out.println("total memory:" + Runtime.getRuntime().totalMemory());
}
}
第一次运行结果:
max memory:2841116672
free memory:190872184
total memory:191889408
分配了1M
max memory:2841116672
free memory:189823592
total memory:191889408
分配了4M
max memory:2841116672
free memory:185629272
total memory:191889408
在Run configurations加入堆分配参数后运行:
参数如下:
-Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
加入堆分配参数后程序运行结果如下:
-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
max memory:20316160
free memory:4445840
total memory:5111808
[GC[DefNew: 650K->128K(1536K), 0.0025108 secs] 650K->470K(4992K), 0.0025555 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
分配了1M
max memory:20316160
free memory:3552472
total memory:5111808
[GC[DefNew: 1180K->0K(1536K), 0.0021683 secs][Tenured: 1494K->1494K(3456K), 0.0041580 secs] 1522K->1494K(4992K), [Perm : 2509K->2509K(21248K)], 0.0063851 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
分配了4M
max memory:20316160
free memory:3637136
total memory:9441280
Heap
def new generation total 1664K, used 108K [0x00000000f9a00000, 0x00000000f9bc0000, 0x00000000fa0a0000)
eden space 1536K, 7% used [0x00000000f9a00000, 0x00000000f9a1b100, 0x00000000f9b80000)
from space 128K, 0% used [0x00000000f9b80000, 0x00000000f9b80000, 0x00000000f9ba0000)
to space 128K, 0% used [0x00000000f9ba0000, 0x00000000f9ba0000, 0x00000000f9bc0000)
tenured generation total 7556K, used 5590K [0x00000000fa0a0000, 0x00000000fa801000, 0x00000000fae00000)
the space 7556K, 73% used [0x00000000fa0a0000, 0x00000000fa615ab8, 0x00000000fa615c00, 0x00000000fa801000)
compacting perm gen total 21248K, used 2519K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 11% used [0x00000000fae00000, 0x00000000fb075eb0, 0x00000000fb076000, 0x00000000fc2c0000)
No shared spaces configured.
在实际工作中,我们可以直接将初始的堆大小与最大堆大小设置相等,这样的好处是可以减少程序运行时的垃圾回收次数,从而提高性能。
2)
新生代的配置
-Xmn:可以设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为有很大的影响,新生代大小一般会设置整个堆空间的1/3到1/4左右。
-XX:SurvivorRatio:用来设置新生代中eden空间和from/to空间的比例。含义:-XX:SurvivorRatio=eden/from=eden/to
测试代码如下:
public class Test02 {
public static void main(String[] args) {
//第一次配置
//-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
//第二次配置
//-Xms20m -Xmx20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
//第三次配置
//-XX:NewRatio=老年代/新生代
//-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
byte[] b = null;
//连续向系统申请10MB空间
for(int i = 0 ; i <10; i ++){
b = new byte[1*1024*1024];
}
}
}
第一次配置jvm参数运行结果:
[GC[DefNew: 512K->256K(768K), 0.0025505 secs] 512K->441K(20224K), 0.0025980 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 768K, used 383K [0x00000000f9a00000, 0x00000000f9b00000, 0x00000000f9b00000)
eden space 512K, 24% used [0x00000000f9a00000, 0x00000000f9a1ff40, 0x00000000f9a80000)
from space 256K, 100% used [0x00000000f9ac0000, 0x00000000f9b00000, 0x00000000f9b00000)
to space 256K, 0% used [0x00000000f9a80000, 0x00000000f9a80000, 0x00000000f9ac0000)
tenured generation total 19456K, used 10425K [0x00000000f9b00000, 0x00000000fae00000, 0x00000000fae00000)
the space 19456K, 53% used [0x00000000f9b00000, 0x00000000fa52e538, 0x00000000fa52e600, 0x00000000fae00000)
compacting perm gen total 21248K, used 2515K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 11% used [0x00000000fae00000, 0x00000000fb074f80, 0x00000000fb075000, 0x00000000fc2c0000)
No shared spaces configured.
第二次配置jvm参数运行结果:
[GC[DefNew: 2694K->1492K(5376K), 0.0026458 secs] 2694K->1492K(18688K), 0.0027057 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GC[DefNew: 4752K->1024K(5376K), 0.0016125 secs] 4752K->1492K(18688K), 0.0016341 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC[DefNew: 4124K->1024K(5376K), 0.0008615 secs] 4593K->1492K(18688K), 0.0008743 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 5376K, used 3162K [0x00000000f9a00000, 0x00000000fa100000, 0x00000000fa100000)
eden space 3584K, 59% used [0x00000000f9a00000, 0x00000000f9c16a80, 0x00000000f9d80000)
from space 1792K, 57% used [0x00000000f9f40000, 0x00000000fa040010, 0x00000000fa100000)
to space 1792K, 0% used [0x00000000f9d80000, 0x00000000f9d80000, 0x00000000f9f40000)
tenured generation total 13312K, used 468K [0x00000000fa100000, 0x00000000fae00000, 0x00000000fae00000)
the space 13312K, 3% used [0x00000000fa100000, 0x00000000fa175270, 0x00000000fa175400, 0x00000000fae00000)
compacting perm gen total 21248K, used 2518K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 11% used [0x00000000fae00000, 0x00000000fb075b00, 0x00000000fb075c00, 0x00000000fc2c0000)
No shared spaces configured.
第三次jvm参数配置的运行结果:
[GC[DefNew: 2690K->1492K(5120K), 0.0021059 secs] 2690K->1492K(18816K), 0.0021442 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC[DefNew: 4745K->1024K(5120K), 0.0015640 secs] 4745K->1492K(18816K), 0.0015817 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC[DefNew: 4123K->1024K(5120K), 0.0008679 secs] 4592K->1492K(18816K), 0.0008817 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 5120K, used 3159K [0x00000000f9a00000, 0x00000000fa0a0000, 0x00000000fa0a0000)
eden space 3456K, 61% used [0x00000000f9a00000, 0x00000000f9c15d10, 0x00000000f9d60000)
from space 1664K, 61% used [0x00000000f9f00000, 0x00000000fa000010, 0x00000000fa0a0000)
to space 1664K, 0% used [0x00000000f9d60000, 0x00000000f9d60000, 0x00000000f9f00000)
tenured generation total 13696K, used 468K [0x00000000fa0a0000, 0x00000000fae00000, 0x00000000fae00000)
the space 13696K, 3% used [0x00000000fa0a0000, 0x00000000fa115270, 0x00000000fa115400, 0x00000000fae00000)
compacting perm gen total 21248K, used 2518K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 11% used [0x00000000fae00000, 0x00000000fb075b00, 0x00000000fb075c00, 0x00000000fc2c0000)
No shared spaces configured.
不同的堆分布情况,对系统执行会产生一定的影响,在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数。
3.堆溢出处理:
在java程序的运行过程中,如果堆空间不足,则会抛出内存溢出的错误(Out Of Menory)OOM,一旦这类问题发生在生产环境,可能引起严重的业务中断,java虚拟机提供了-XX:+HeapDumpOnOutOfMemoryError,使用该参数可以在内存溢出时导出整个堆信息,与之配合使用的还有参数,
-XX:HeapDumpPath,可以设置导出堆的存放路径。
内存分析工具:Memory Analyzer 1.5.0
测试代码如下:
import java.util.Vector;
public class Test03 {
public static void main(String[] args) {
//-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Test03.dump
//堆内存溢出
Vector v = new Vector();
for(int i=0; i < 5; i ++){
v.add(new Byte[1*1024*1024]);
}
}
}
这里初始化堆只分配了1M内存,但是程序需要5M内存,所以会产生堆内存溢出。
运行结果如下:
Error occurred during initialization of VM
Too small initial heap for new size specified
4.java栈jvm参数配置:
Java虚拟机提供了参数-Xss来指定线程的最大栈空间,整个参数也直接决定了函数可调用的最大深度。
测试代码如下:
public class Test04 {
//-Xss1m
//-Xss5m
//栈调用深度
private static int count;
public static void recursion(){
count++;
recursion();
}
public static void main(String[] args){
try {
recursion();
} catch (Throwable t) {
System.out.println("调用最大深入:" + count);
t.printStackTrace();
}
}
}
给栈内存配置为1M时的运行结果:-Xss1m
调用最大深入:12552
java.lang.StackOverflowError
at com.bjsxt.base001.Test04.recursion(Test04.java:12)
at com.bjsxt.base001.Test04.recursion(Test04.java:13)
给栈内存配置为1M时的运行结果:-Xss5m
调用最大深入:64989
java.lang.StackOverflowError
at com.bjsxt.base001.Test04.recursion(Test04.java:12)
at com.bjsxt.base001.Test04.recursion(Test04.java:13)
at com.bjsxt.base001.Test04.recursion(Test04.java:13)
5.方法区jvm参数配置:
和java堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息,方法区(永久区)可以保存多少信息可以对其进行配置,在默认情况下,-XX:MaxPermSize为64MB,如果系统运行时生产大量的类,就需要设置一个相对合适的方法区,以免出现永久区内存溢出的问题。
-XX:PermSize=64M -XX:MaxPermSize=64M
6.直接内存配置:
直接内存也是java程序中非常重要的组成部分,特别是广泛用在NIO中,直接内存跳过了java堆,使java程序可以直接访问原生堆空间,因此在一定程度上加快了内存空间的访问速度。但是说直接内存一定就可以提高内存访问速度也不见得,具体情况具体分析。
相关配置参数:-XX:MaxDirectMemorySize,如果不设置默认值为最大堆空间,即-Xmx。直接内存使用达到上限时,就会触发垃圾回收,如果不能有效的释放空间,也会引起系统的OOM.
7.虚拟机工作模式Client和Server:
目前java虚拟机支持Client和Server两种运行模式,使用参数-client可以指定使用Client模式,使用-server即使用Server模式。可以直接在命令行查看当前计算机系统自动选择的运行模式。java - version即可。
二者区别:Client模式相对Server启动较快,如果不追求系统的长时间使用性能仅仅是测试,可以使用Client模式。而Server模式则启动比较慢,原因是会对其进行复杂的系统性能信息收集和使用更复杂的算法对程序进行优化,一般我们的生产环境都会使用Server模式,长期运行其性能要远远快于Client模式。