参考书籍: 实战JAVA虚拟机 JVM故障诊断与性能优化
参考unixboy的博客,网址:https://unixboy.iteye.com/blog/174173/
JVM中最大堆大小有三方面的限制,操作系统、系统可用虚拟内存限制、系统可用物理内存限制。32位系统,一般限制在1.5G-2G,64位系统无限制。
典型设置:
一、java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:设置JVM最大可用内存为3550m
-Xms3550m:设置JVM初始内存为3550m,此值可与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置nian年轻代大小为2G。整个JVM内存大小=年轻代大小+年老代大小+持久代大小。持久代大小一般固定为64M。
Sun官方推荐配置为整个堆的3/8。
-Xss128k: 设置每个线程的最大栈空间,这个参数直接决定了函数的调用深度。JDK5.0以后每个线程堆栈为1M,以前每个线程堆栈大小为256k。相同物理内存下,减少这个值能生成更多的线程,但是操作系统对一个进程内的线程还是有限制的,不能无限生成,经验值在3000-5000左右。
二、java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4 :设置年轻代(Eden区和SurvivorRatio)与年老代的比值(除去持久代)。故年轻代:年老代=1:4,年轻代占整个堆栈的1/5。
-XX:SurvivorRatio=4 :设置年轻代中Survivor与Eden的比值,大小为4,即Survivor与Eden的比值=2:4,即一个survivor占整个 年轻代的1/6,。
-XX:MaxPermSize=16m 设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄,如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
Ratio:比例,系数
survivor:sheg生还者,幸存者
三、JVM给了三种选择:串行收集器、并行收集器、并发收集器。
吞吐量优先的并行收集器,适用于科学技术和后台处理等。
典型配置:
java -Xmx3100m -Xms3100m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
-XX:+UseParallelGC:选择垃圾收集器为并行收集器,此配置对年轻代有效,即年轻代使用并行收集,年老代仍然是串行收集。
-XX:ParallelGCThreads=20 配置并行收集器的线程数,此值最好配置与处理器数目相等。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
-XX:+UseParallelOldGC 配置年老代使用并行收集,JDK6.0支持对年老代并行收集。
- java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。 - java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
响应时间优先的并发收集器
如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
典型配置:
- java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。 - java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片
mark:记号,concurrent同时发生的
- ParaellGC是Stop the application,之后多个线程去收集可回收的内存,之后执行回收操作,关键点是:确认哪些内存可以回收、之后再回收,俩个操作都在Stop application后才执行的。
- CMS也是Stop the application,但是:初始标记,确认哪些内存可以被回收是与业务线程同时执行的;之后是重新标记阶段,这个阶段会Stop the application;之后是回收阶段,这个阶段是和业务线程同时执行着的。
1 获取运行环境的Xmx
System.out.println("-Xmx"+Runtime.getRuntime().maxMemory()/1000/1000+"M");
-Xmx1888M
2 堆,方法区和Java栈存放
public class SimpleHeap {
private int id;
public SimpleHeap(int id){
this.id=id;
}
public void show(){
System.out.println("My ID is "+id);
}
public static void main(String[] args) {
SimpleHeap s1=new SimpleHeap(1);
SimpleHeap s2=new SimpleHeap(2);
s1.show();
s2.show();
}
}
3 Java堆分为新生代和老年代
新生代分eden区,s0区(from区)、s1区(to区)。老年代为tenured区。
-Xmx20m -Xms5m -XX:+PrintCommandLineFlags -XX:PrintGCDetails -XX:+UseSerialGC
具体分配如下:实际输出为:maxMemory=20316160 bytes
当前最大内存由:-XX:MaxHeapSize=20971520 即为:20*1024*1024=20971520
实际可用内存会浪费大小等于from/to的空间,-Xmx的值减去from的大小,from大小为131072,此时
20971520-131072=20840488,仍然有偏差,因需进一步做了对齐操作。
4 Java栈
Java是线程私有的内存空间,和线程有关。Java堆和程序有关。
-Xss指定线程的最大栈空间,决定了函数调用的最大深度。
如:
public class TestStackDeep {
private static int count=0;
public static void recursion(){
count++;
recursion();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
recursion();
}catch(Throwable e){
System.out.println("deep of calling ="+count);
e.printStackTrace();
}
}
}
修改-Xss的值,可以增加循环的次数。同时如果减少recursion方法中的局部变量,可以增加循环的次数。
5 方法区
在JDK1.6和JDK1.7中,方法区可理解为永久区(Perm),永久区可以使用参数-XX:PermSize和-XX:MaxPermSize指定。默认-XX:MaxPermSize为64M。
-XX:+PrintGCDetails -XX:PermSize=5M -XX:MaxPermSize=5m.
在JDK1.8中,永久区被彻底移除,而是元数据区,大小可以使用-XX:MaxMetaspaceSize指定,可以加载更多的类,这是一块堆外的直接内存。与永久区不同,如果不指定大小,默认会耗尽所有的可用系统内存。
第三章:
1 打印GC的日志
-XX:+PrintGC,只要有GC就会打印日志。
-XX:+PrintGCDetails,会打印更加详细的GC日志。
-XX:+PrintHeapAtGC 会在每次GC前后分别打印堆的信息。
-XX:+PrintGCTimeStamps 每次GC时,额外输出GC发生的时间。
-XX:+PrintGCApplicationConcurrentTime可以打印程序的执行时间
-XX:+PrintGCApplicationStoppedTime应用程序由于GC 而产生的停顿时间。
-XX:+TraceClassLoading 跟踪类的加载
-XX:+TraceClassUnloading 跟踪类的卸载
-XX:+PrintVMOptions
-Xmn可以设置新生代的大小,新生代的大小一般设置为整个堆空间的1/3到1/4左右。
-XX:SurvivorRatio用来设置新生代中eden空间和from/to空间的比例关系。
-XX:SurvivorRatio=eden/from=eden/to
(50页)
具体实例:
public class NewSizeDemo {
public static void main(String[] args) {
byte[] b=null;
for(int i=0;i<10;i++){
b=new byte[1*1024*1024];
}
}
}
1 -Xmx20m -Xms20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails
eden与from的比值为2:1,故eden区为512KB,其他from和to分别为256KB
2 下面的发生了3次新生代的GC
[GC [PSYoungGen: 2702K->1656K(5376K)] 2702K->1656K(18688K), 0.0074134 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC [PSYoungGen: 4771K->1640K(5376K)] 4771K->1640K(18688K), 0.0005884 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [PSYoungGen: 4740K->1576K(5376K)] 4740K->1576K(18688K), 0.0004804 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 5376K, used 3858K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 3584K, 63% used [0x00000000ff900000,0x00000000ffb3a8d0,0x00000000ffc80000)
from space 1792K, 87% used [0x00000000ffc80000,0x00000000ffe0a030,0x00000000ffe40000)
to space 1792K, 0% used [0x00000000ffe40000,0x00000000ffe40000,0x0000000100000000)
ParOldGen total 13312K, used 0K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff900000)
PSPermGen total 21248K, used 2561K [0x00000000f9a00000, 0x00000000faec0000, 0x00000000fec00000)
object space 21248K, 12% used [0x00000000f9a00000,0x00000000f9c80438,0x00000000faec0000)
3 -Xmx20m -Xms20m -XX:NewRatio=2 -XX:+PrintGCDetails
-XX:NewRatio=老年代/新生代,此新生代即为20/3=6m左右,老年代13m左右。