官方手册:
http://docs.oracle.com/javase/7/docs/ ---->
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/java.html java命令的各种选项的说明
参考书籍:
《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》
首先说下JVM的内存堆结构,看下图:
主要由 方法区Permanent Generation + 新生代Eden + 新生代幸存区S0和S1 + 老年代Old Generation;
大部分新生成的对象都放在Eden中,当Eden内存不够用时,触发Young GC,这时,会将Eden中不能被释放的对象以及S0中幸存的对象,都Copy到S1中,并且将经历过几次Young GC还幸存的新生代对象,放入到Old Generation中,然后释放Eden和S0;
所以S0和S1是来回切换使用的,保存新生代中还不能被释放的对象,所以S0和S1总有一个会是空的,当然在发生Youg GC时,对象正在COPY时会是二者都有数据;
如果经历几次Young GC时新生代还是满的,还不能够接收Eden中过来的幸存对象,就会抛出java.lang.OutOfMemorryError:java heap space;
如果在进行Youg GC时,发现S0不够用时,则直接将对象放入Old Generation中,这时如果Old Generation内存也不够时,则触发Full GC,Full GC后如果Old Generation还是满的,就抛出内存溢出异常:
java.lang.OutOfMemorryError:java heap space;
所以,假设有一个永不销毁的对象,其经历的过程如下:首先在创建时放入Eden,当某个时刻Eden满了时,通过Young GC放入S0或者S1,其在S0或者S1经历过几次Youg GC后,放入到Old generation中,当Old Generation满了的时候,发生Full GC.
这里有点需要注意,代码区(方法区),并不属于堆空间,他是一个单独的空间,其中保存有虚拟机自己的静态数据,以及加载的Class类级别静态对象,如class本身,method,field等,当然如果这部分空间不足时,一样会触发Full GC.而且如果GC之后还是满的,就会抛出PermGen Space异常
上面说到了堆和方法区,接下来我们看下完整的JVM内存结构:
之前说了堆结构和代码区Permanent Space,顺着这个内存结构图,说下Code Generation,JVM自己内部使用的一块区域,用来编译和保存本地代码(native code),基本不会导致内存异常。如果该操作没有足够的空间,JVM可能会导致崩溃
Socket Buffers:用来做网络通信的缓冲区,分为发送去和接收区,需要在java代码中控制它,所以外部无法配置。这里如果满了的话,会导致IOException: Too many open files 。
Thread JVM Stack:java的线程栈,java分配一个对象时,对象的具体内容在堆中,而对象的引用则位于栈中,也就是这里。而且方法的局部变量以及函数地址的调入和调出,都存放在这里,所以,JAVA的一个对象,有两部分,对象本身的值在堆中,而引用则在栈中。当栈空间不够时,比如递归层次太深,就会导致java.lang.StackOverflowError异常
Direct Memory Space:他可以让开发人员映射内存到java Object Heap外
JNI Code :JNI code本身使用的内存非常小。
JNI allocate memory:JNI 程序本身也需要分配内存。
Garbage Collection:其实GC也是需要内存的,gc线程的消耗以及存放GC所缓存的信息。
所以,java进程实际占用并不等于堆内存的大小,很显然还有栈呀代码区呀什么之类的内存的大小。
再看一个列子,一个进程,设置的启动参数中,堆的最大内存指定为30G,而通过TOP命令,查看的内存使用情况为:
显然,这里的虚拟内存大小变成了38G,远远超过指定的堆的大小30G,究其原因,一是,进程占用的内存除了堆,还有栈空间之类的,就是上面标红的一部分,二是,虚拟内存表示java像操作系统申请了38G,但是实际使用只有27G。在操作系统级别,内存是按页分配的,而且,如果你申请了空间,不访问它,那么虚拟内存使用大小是你申请的大小,而RES却是0,当你访问时,如果物理内存不足,就有可能导致要访问的内存页不在物理内存里,会发生缺页中断,发生CPU的上下文切换,性能会下降,而这时RES才会增大。而且从经验来看Xms和Xmx是告诉JAVA,我实际要使用的堆的最大值是多少,也就是RES是多少,也就是说我实际有可能要放28G的对象进来,而我们知道JVM的堆空间,S0和S1是切换的,所以,java实际需要向操作系统申请的空间肯定大于28G,但是实际进程使用的却只有27G,这就是为什么上图显示RES为27G而VIRT是38G。
jstat的总体使用参数说明
Option | Displays… |
---|---|
class | class loader的行为统计。Statistics on the behavior of the class loader. |
compiler | HotSpt JIT编译器行为统计。Statistics of the behavior of the HotSpot Just-in-Time compiler. |
gc | 垃圾回收堆的行为统计。Statistics of the behavior of the garbage collected heap. |
gccapacity | 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计。Statistics of the capacities of the generations and their corresponding spaces. |
gccause | 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因。Summary of garbage collection statistics (same as -gcutil), with the cause of the last and current (if applicable) garbage collection events. |
gcnew | 新生代行为统计。Statistics of the behavior of the new generation. |
gcnewcapacity | 新生代与其相应的内存空间的统计。Statistics of the sizes of the new generations and its corresponding spaces. |
gcold | 年老代和永生代行为统计。Statistics of the behavior of the old and permanent generations. |
gcoldcapacity | 年老代行为统计。Statistics of the sizes of the old generation. |
gcpermcapacity | 永生代行为统计。Statistics of the sizes of the permanent generation. |
gcutil | 垃圾回收统计概述。Summary of garbage collection statistics. |
printcompilation | HotSpot编译方法统计。HotSpot compilation method statistics. |
-h
n每隔
n个样本 (行),
n是正整数,
缺省为0,代表只有第一行为列头。
-t
n第一行输出为时间戳,
它表示自从目标JVM启动以来逝去的时间,以秒为单位。
如何查看JVM的性能呢?现在给出实际的列子:
1、查看java命令启动参数
jinfo 20611
Attaching to process ID 20611, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 19.1-b02
Java System Properties:
java.runtime.name = Java(TM) SE Runtime Environment
sun.boot.library.path = /usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/amd64
java.vm.version = 19.1-b02
java.vm.vendor = Sun Microsystems Inc.
java.vendor.url = http://java.sun.com/
path.separator = :
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
file.encoding.pkg = sun.io
sun.java.launcher = SUN_STANDARD
user.country = US
sun.os.patch.level = unknown
java.vm.specification.name = Java Virtual Machine Specification
user.dir = /mezi/dps/local/investopedia-market-service
java.runtime.version = 1.6.0_24-b07
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
java.endorsed.dirs = /usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/endorsed
os.arch = amd64
java.io.tmpdir = /tmp
line.separator =
java.vm.specification.vendor = Sun Microsystems Inc.
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/amd64/server:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/amd64:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.specification.name = Java Platform API Specification
java.class.version = 50.0
sun.management.compiler = HotSpot 64-Bit Server Compiler
os.version = 2.6.18-348.12.1.el5
user.home = /home/mmdps
user.timezone = UTC
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.6
java.class.path = :libs/aopalliance.jar:libs/axiom-api-1.2.13.jar:libs/axiom-impl-1.2.13.jar:libs/axis2-adb-1.6.2.jar:libs/axis2-kernel-1.6.2.jar:libs/axis2-transport-http-1.6.2.jar:libs/axis2-transport-local-1.6.2.jar:libs/axis2-xmlbeans-1.6.2.jar:libs/bonecp-0.7.1.RELEASE.jar:libs/commons-codec-1.3.jar:libs/commons-httpclient-3.1.jar:libs/commons-lang-2.5.jar:libs/ehcache-2.7.1.jar:libs/google-collections-1.0.jar:libs/gson-2.2.4.jar:libs/guice-3.0.jar:libs/httpcore-4.0.jar:libs/javax.inject.jar:libs/jetty-client-8.1.10.v20130312.jar:libs/jetty-continuation-8.1.10.v20130312.jar:libs/jetty-http-8.1.10.v20130312.jar:libs/jetty-io-8.1.10.v20130312.jar:libs/jetty-security-8.1.10.v20130312.jar:libs/jetty-server-8.1.10.v20130312.jar:libs/jetty-servlet-8.1.10.v20130312.jar:libs/jetty-util-8.1.10.v20130312.jar:libs/jetty-xml-8.1.10.v20130312.jar:libs/json-rpc-1.0.jar:libs/log4j-1.2.15.jar:libs/mail-1.4.jar:libs/mockito-all-1.9.0.jar:libs/mysql-connector-java-5.1.7-bin.jar:libs/neethi-3.0.2.jar:libs/quartz.jar:libs/servlet-api-3.0.jar:libs/slf4j-api-1.7.5.jar:libs/slf4j-log4j12-1.7.5.jar:libs/start.jar:libs/wsdl4j-1.6.2.jar:libs/xmlbeans-2.3.0.jar:libs/XmlSchema-1.4.7.jar:properties:resources
user.name = mmdps
java.vm.specification.version = 1.0
java.home = /usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre
sun.arch.data.model = 64
user.language = en
java.specification.vendor = Sun Microsystems Inc.
java.vm.info = mixed mode
java.version = 1.6.0_24
java.ext.dirs = /usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path = /usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/resources.jar:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/rt.jar:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/jce.jar:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/lib/modules/jdk.boot.jar:/usr/lib/jvm/java-1.6.0-sun-1.6.0.24.x86_64/jre/classes
java.vendor = Sun Microsystems Inc.
file.separator = /
java.vendor.url.bug = http://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.cpu.isalist =
VM Flags:
-Xms2000m -Xmx8000m
2、 jstat -gc pid 查看垃圾回收统计
jstat -gc 50206 500 10
列名 | 描述 |
---|---|
S0C | 当前survivor space 0容量。Current survivor space 0 capacity (KB). |
S1C | survivor space 1容量。Current survivor space 1 capacity (KB). |
S0U | Survivor space 0 利用情况。Survivor space 0 utilization (KB). |
S1U | Survivor space 1 利用情况。Survivor space 1 utilization (KB). |
EC | 当前新生代eden空间容量。Current eden space capacity (KB). |
EU | 新生代eden空间利用情况。Eden space utilization (KB). |
OC | 当前年老代空间容量。Current old space capacity (KB). |
OU | 年老代利用情况。Old space utilization (KB). |
PC | 当前永生代空间容量。Current permanent space capacity (KB). |
PU | 永生代空间利用情况。Permanent space utilization (KB). |
YGC | 新生代GC事件次数。 Number of young generation GC Events. |
YGCT | 新生代GC耗时。Young generation garbage collection time. |
FGC | full GC次数。Number of full GC events. |
FGCT | full gc耗时。Full garbage collection time. |
GCT | 总GC耗时。Total garbage collection time. |
3、GC内存空间使用统计
jstat -gccapacity 20611 500 5
列名 | 描述 |
---|---|
NGCMN | 最小新生代容量。Minimum new generation capacity (KB). |
NGCMX | 最大新生代容量。Maximum new generation capacity (KB). |
NGC | 当前新生代容量。Current new generation capacity (KB). |
S0C | 当前新生代survivor 0区容量。Current survivor space 0 capacity (KB). |
S1C | 当前新生代survivor 1区容量。Current survivor space 1 capacity (KB). |
EC | 当前新生代eden空间容量。Current eden space capacity (KB). |
OGCMN | 最小年老代容量。Minimum old generation capacity (KB). |
OGCMX | 最大年老代容量。Maximum old generation capacity (KB). |
OGC | 当前年老代容量。Current old generation capacity (KB). |
OC | 当前年老代空间容量。Current old space capacity (KB). |
PGCMN | 最小永生代容量。Minimum permanent generation capacity (KB). |
PGCMX | 最大永生代容量。Maximum Permanent generation capacity (KB). |
PGC | 当前新生成的永生代容量。Current Permanent generation capacity (KB). |
PC | 当前永生代空间容量。Current Permanent space capacity (KB). |
YGC | 新生代GC次数。Number of Young generation GC Events. |
FGC | Full GC次数。Number of Full GC Events. |
4、GC内存空间各区使用比例
jstat -gcutil 20611 500 5
列名 | 描述 |
---|---|
S0 | survivor 0区利用率。 Survivor space 0 utilization as a percentage of the space’s current capacity. |
S1 | survivor 1区利用率。 Survivor space 1 utilization as a percentage of the space’s current capacity. |
E | eden区利用率。 Eden space utilization as a percentage of the space’s current capacity. |
O | 年老代空间利用率。 Old space utilization as a percentage of the space’s current capacity. |
P | 永生代空间利用率。Permanent space utilization as a percentage of the space’s current capacity. |
YGC | young gc次数。 Number of young generation GC events. |
YGCT | young gc耗时。 Young generation garbage collection time. |
FGC | full gc次数。 Number of full GC events. |
FGCT | full gc耗时。 Full garbage collection time. |
GCT | GC总耗时。 Total garbage collection time. |
5、GC内存空间的GC原因
与-gcutil相同,多两列,一个是最后一次GC原因,一个是本次GC原因
jstat -gccause 20611 500 5
列名 | 描述 |
---|---|
LGCC | 上次GC原因。Cause of last Garbage Collection. |
GCC | 本次GC原因。Cause of current Garbage Collection. |
6、新生代使用情况
jstat -gcnew 20611 500 5
列名 | 描述 |
---|---|
S0C | 当前survivor 0区容量。Current survivor space 0 capacity (KB). |
S1C | 当前survivor 1区容量。Current survivor space 1 capacity (KB). |
S0U | 当前survivor 0区利用情况。 Survivor space 0 utilization (KB). |
S1U | 当前survivor 1区利用情况。 Survivor space 1 utilization (KB). |
TT | 阀值, 用于控制对象在新生代存活的次数。 Tenuring threshold. |
MTT | 阀值, 用于控制对象在新生代存活的最大次数。Maximum tenuring threshold. |
DSS | 期望存活大小。 Desired survivor size (KB). |
EC | 当前eden空间容量。Current eden space capacity (KB). |
EU | eden空间利用情况。 Eden space utilization (KB). |
YGC | 年轻代gc次数。Number of young generation GC events. |
YGCT | 年轻代GC耗时。 Young generation garbage collection time. |
note: 若某个age上的survivor space对象的大小如果超过Desired survivor size,则重新计算tenuring threshold,以age和MaxTenuringThreshold的最小值为准
7、新生代空间统计
jstat -gcnewcapacity 20611 500 5
列名 | 描述 |
---|---|
NGCMN | 最小新生代容量。Minimum new generation capacity (KB). |
NGCMX | 最大新生代容量。Maximum new generation capacity (KB). |
NGC | 当前新生代容量。Current new generation capacity (KB). |
S0CMX | 最大survivor 0区容量。Maximum survivor space 0 capacity (KB). |
S0C | 当前survivor 0区容量。Current survivor space 0 capacity (KB). |
S1CMX | 最大survivor 1区容量。Maximum survivor space 1 capacity (KB). |
S1C | 当前survivor 1区容量。Current survivor space 1 capacity (KB). |
ECMX | 最大eden区容量。Maximum eden space capacity (KB). |
EC | 当前eden区容量。Current eden space capacity (KB). |
YGC | young gc次数。 Number of young generation GC events. |
FGC | full gc次数。Number of Full GC Events. |
8、年老代使用情况
jstat -gcold 20611 500 5
列名 | 描述 |
---|---|
PC | 当前永久代空间容量。Current permanent space capacity (KB). |
PU | 永久代空间利用情况。Permanent space utilization (KB). |
OC | 当前年老代空间容量。Current old space capacity (KB). |
OU | 年老代空间利用情况。 old space utilization (KB). |
YGC | young gc次数。 Number of young generation GC events. |
FGC | full gc次数。Number of full GC events. |
FGCT | full gc耗时。 Full garbage collection time. |
GCT | gc总耗时。 Total garbage collection time. |
9、年老代空间容量
jstat -gcoldcapacity 20611 500 5
列名 | 描述 |
---|---|
OGCMN | 最小年老代容量。Minimum old generation capacity (KB). |
OGCMX | 最大年老代容量。Maximum old generation capacity (KB). |
OGC | 当前年老代容量。Current old generation capacity (KB). |
OC | 当前年老代空间容量。Current old space capacity (KB). |
YGC | young gc次数。 Number of young generation GC events. |
FGC | full gc次数。 Number of full GC events. |
FGCT | full gc耗时。Full garbage collection time. |
GCT | 总GC耗时。 Total garbage collection time. |
10、永生代统计
jstat -gcpermcapacity 20611 500 5
列名 | 描述 |
---|---|
PGCMN | Minimum permanent generation capacity (KB). |
PGCMX | Maximum permanent generation capacity (KB). |
PGC | Current permanent generation capacity (KB). |
PC | Current permanent space capacity (KB). |
YGC | Number of young generation GC events. |
FGC | Number of full GC events. |
FGCT | Full garbage collection time. |
GCT | Total garbage collection time. |
11、编译统计
jstat -printcompilation 20611 500 5
列名 | 描述 |
---|---|
Compiled | 执行的编译任务次数 |
Size | Number of bytes of bytecode for the method. |
Type | 编译类型。Compilation type. |
Method | 类名和方法名。类名使用”/”代替了原命名空间符号”.” |
12、类加载情况
jstat -class 20611 500 5
列名 | 描述 |
---|---|
Loaded | 已加载的类个数。 |
Bytes | 已加载类占用字节数(KB为单位)。 |
Unloaded | 卸载的类个数。 |
Bytes | 卸载的类占用字节数(KB为单位)。 |
Time | 加载和卸载操作花费的时间。 |
13、栈大小的调整
jinfo flag
ThreadStackSize
20611
:-XX:ThreadStackSize=1024
按照以上这些数据,我们做下内存分布的分析
先看持久代,参数设置为:XX:PermSize 最小值,XX:MaxpermSize最大值
使用 jinfo -flag PermSize 20611 + jinfo -flag MaxPermSize 20611 看到值分别为:
-XX:PermSize=21757952 -XX:MaxPermSize=85983232,因为上面的进程,我们没配置它,所以这两个值就是默认值,一个是20.75M,一个是82M,
这个与
jstat -gcpermcapacity 20611 500 5命令中的
PGCMN =
21248.0 KB 和
PGCMX=
83968.0 KB是相吻合的,同时我们看到当前容量为
31M,
所以,这个总容量是没太大问题的,但是这里会发生在GC之后内存收缩情况,
所以我们一般都设置成 XX:PermSize=XX:MaxPermSize
实验1(持久代),我们用参数配置后再来启动它,
-XX:PermSize=50M -XX:MaxPermSize=100M,使用 jstat -gcpermcapacity命令后发现PGCMN=51200
PGCMX=102400,PGC=51200,所以,和我们期望的是一样的,但是如果想看我们实际占用了多少呢?通过jstat -gc 19539 命令,可以发现
PC=51200,PU=29174.4,说明我们实际是用了29M
。
反过来,因为我们知道我们的进程需要占用29M的空间,当我们把 -XX:PermSize=10M -XX:MaxPermSize=10M配置成最多使用10M时,启动进程就会发现报了如下错误:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
看下新生代区,一共有这几个参数可以影响到其大小:
指定年轻代的大小(Eden+S0+S1);
-XX:NewSize设置年轻代初始化值大小;
-XX:MaxNewSize设置年轻代最大值,
-XX:NewRatio年轻代与年老代的比值,
Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置;
-XX:SurvivorRatio设置
Survivor
与
Eden
之间的比值,如果设置为8,则表示
一个Surivior(S0)为1,Eden为8,所以,S0占年轻代的1/10,S1占1/10,Eden占8/10
先使用jinfo -falg看下这些值的默认配置:-XX:NewSize=1310720 -XX:MaxNewSize=18446744073709486080(无限制) -XX:NewRatio=2 -XX:SurvivorRatio=8
当我使用java -server -
Xmn3000M
-XX:SurvivorRatio=8 -XX:NewSize=3000M -XX:MaxNewSize=3000M -XX:PermSize=500M -XX:MaxPermSize=500M
-Xms2000m
-Xmx8000m该参数时启动会报错,
Error occurred during initialization of VM ,Too small initial heap for new size specified
,原因在于-Xms指定初始化堆大小为2000M,而最小新生代
-Xmn3000
都比该值大,这个是不允许的,所以,
调整后的参数为:java -server -Xmn3000M -XX:SurvivorRatio=8 -XX:NewSize=3000M -XX:MaxNewSize=3000M -XX:PermSize=500M -XX:MaxPermSize=500M -Xms8000m -Xmx8000m
使用jstat -gcnewcapacity 26111命令发现,最小最大新生代的大小
确实
都是3000M,但奇怪的是S0\S1\Eden的最大值也都是3000M,Eden实际容量是2400M,
这说明这种情况下
-
XX:SurvivorRatio=8这个参数不起作用了。
而一般我们配置
XX:SurvivorRatio=1从而使
新生代Eden=S0=S1,并且Xmn设置新生代的大小
调整后的参数为:-Xmn3000M -XX:SurvivorRatio=1 -XX:PermSize=500M -XX:MaxPermSize=500M -Xms8000m -Xmx8000m
这时候再使用gcnewcapacity就会发现:NGCMN=NGCMX=NGC=3000M,lS0C=S1C=EC=1024000,跟我们预期的完全一样!
但是运行一段时间以后,发现值稍微有了一些变化,S0C=1024000,S1C=836224,EC=1360512,就是EC变大了,而S1变小了,至于为什么,不得而已???
看下老年区
老年区没得设置,因为其空间大小就是堆的总空间减去年轻代的空间
其他的一些设置:
-XX:+UseConcMarkSweepGC 使用CMS模式进行垃圾回收,该机制的特点是并发收集、低停顿,但同时,也会产生一些碎片和浮动垃圾,因为CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会有新的垃这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉它们,只好留待下一次GC时再将其清理掉,这部分就是浮动垃圾。
-XX:+UseCMSCompactAtFullCollection,在FULL GC的时候, 对年老代的压缩。由于启动了CMS机制进行垃圾收集,其会产生碎片,所以圾不断产生,所以该选择需要配合上面这个选择,CMS收集器一起使用-XX:CMSInitiatingOccupancyFraction=70 使用cms作为垃圾回收,使用70%后开始CMS收集。为了保证不出现promotion failed(见下面介绍)错误,该值的设置需要满足以下公式CMSInitiatingOccupancyFraction计算公式
CMSInitiatingOccupancyFraction值与Xmn的关系公式
上面介绍了promontion faild产生的原因是EDEN空间不足的情况下将EDEN与From survivor中的存活对象存入To survivor区时,To survivor区的空间不足,再次晋升到old gen区,而old gen区内存也不够的情况下产生了promontion faild从而导致full gc.那可以推断出:eden+from survivor < old gen区剩余内存时,不会出现promontion faild的情况,即:
(Xmx-Xmn)*(1-CMSInitiatingOccupancyFraction/100)>=(Xmn-Xmn/(SurvivorRatior+2)) 进而推断出:CMSInitiatingOccupancyFraction <=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100
-XX:+UseParNewGC | 设置年轻代为并行收集 | 可与CMS收集同时使用 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值 |
|
-XX:ParallelGCThreads | 并行收集器的线程数 | 此值最好配置与处理器数目相等 同样适用于CMS |
网上一个很NB的配置参考:$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xmx3000M -Xms3000M -Xmn600M -XX:PermSize=500M -XX:MaxPermSize=500M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log ";64位jdk参考设置,年老代涨得很慢,CMS执行频率变小,CMS没有停滞,也不会有promotion failed问题,内存回收得很干净
很有用的一个各参数说明:
JVM参数的含义 实例见实例分析
参数名称 | 含义 | 默认值 | |
-Xms | 初始堆大小 | 物理内存的1/64(<1GB) | 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制. |
-Xmx | 最大堆大小 | 物理内存的1/4(<1GB) | 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制 |
-Xmn | 年轻代大小(1.4or lator) | 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小. 增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8 | |
-XX:NewSize | 设置年轻代大小(for 1.3/1.4) | ||
-XX:MaxNewSize | 年轻代最大值(for 1.3/1.4) | ||
-XX:PermSize | 设置持久代(perm gen)初始值 | 物理内存的1/64 | |
-XX:MaxPermSize | 设置持久代最大值 | 物理内存的1/4 | |
-Xss | 每个线程的堆栈大小 | JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右 一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。(校长) 和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"” -Xss is translated in a VM flag named ThreadStackSize” 一般设置这个值就可以了。 | |
-XX:ThreadStackSize | Thread Stack Size | (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.] | |
-XX:NewRatio | 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) | -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。 | |
-XX:SurvivorRatio | Eden区与Survivor区的大小比值 | 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10 | |
-XX:LargePageSizeInBytes | 内存页的大小不可设置过大, 会影响Perm的大小 | =128m | |
-XX:+UseFastAccessorMethods | 原始类型的快速优化 | ||
-XX:+DisableExplicitGC | 关闭System.gc() | 这个参数需要严格的测试 | |
-XX:MaxTenuringThreshold | 垃圾最大年龄 | 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率 该参数只有在串行GC时才有效. | |
-XX:+AggressiveOpts | 加快编译 | ||
-XX:+UseBiasedLocking | 锁机制的性能改善 | ||
-Xnoclassgc | 禁用垃圾回收 | ||
-XX:SoftRefLRUPolicyMSPerMB | 每兆堆空闲空间中SoftReference的存活时间 | 1s | softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap |
-XX:PretenureSizeThreshold | 对象超过多大是直接在旧生代分配 | 0 | 单位字节 新生代采用Parallel Scavenge GC时无效 另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象. |
-XX:TLABWasteTargetPercent | TLAB占eden区的百分比 | 1% | |
-XX:+CollectGen0First | FullGC时是否先YGC | false |
-XX:+UseParallelGC | Full GC采用parallel MSC (此项待验证) | 选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集.(此项待验证) | |
-XX:+UseParNewGC | 设置年轻代为并行收集 | 可与CMS收集同时使用 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值 | |
-XX:ParallelGCThreads | 并行收集器的线程数 | 此值最好配置与处理器数目相等 同样适用于CMS | |
-XX:+UseParallelOldGC | 年老代垃圾收集方式为并行收集(Parallel Compacting) | 这个是JAVA 6出现的参数选项 | |
-XX:MaxGCPauseMillis | 每次年轻代垃圾回收的最长时间(最大暂停时间) | 如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值. | |
-XX:+UseAdaptiveSizePolicy | 自动选择年轻代区大小和相应的Survivor区比例 | 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开. | |
-XX:GCTimeRatio | 设置垃圾回收时间占程序运行时间的百分比 | 公式为1/(1+n) | |
-XX:+ScavengeBeforeFullGC | Full GC前调用YGC | true | Do young generation GC prior to a full GC. (Introduced in 1.4.1.) |
-XX:+UseConcMarkSweepGC | 使用CMS内存收集 | 测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明.所以,此时年轻代大小最好用-Xmn设置.??? | |
-XX:+AggressiveHeap | 试图是使用大量的物理内存 长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 至少需要256MB内存 大量的CPU/内存, (在1.4.1在4CPU的机器上已经显示有提升) | ||
-XX:CMSFullGCsBeforeCompaction | 多少次后进行内存压缩 | 由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生"碎片",使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理. | |
-XX:+CMSParallelRemarkEnabled | 降低标记停顿 | ||
-XX+UseCMSCompactAtFullCollection | 在FULL GC的时候, 对年老代的压缩 | CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。 可能会影响性能,但是可以消除碎片 | |
-XX:+UseCMSInitiatingOccupancyOnly | 使用手动定义初始化定义开始CMS收集 | 禁止hostspot自行触发CMS GC | |
-XX:CMSInitiatingOccupancyFraction=70 | 使用cms作为垃圾回收 使用70%后开始CMS收集 | 92 | 为了保证不出现promotion failed(见下面介绍)错误,该值的设置需要满足以下公式CMSInitiatingOccupancyFraction计算公式 |
-XX:CMSInitiatingPermOccupancyFraction | 设置Perm Gen使用到达多少比率时触发 | 92 | |
-XX:+CMSIncrementalMode | 设置为增量模式 | 用于单CPU情况 | |
-XX:+CMSClassUnloadingEnabled |
-XX:+PrintGC | 输出形式: [GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs] | ||
-XX:+PrintGCDetails | 输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs] | ||
-XX:+PrintGCTimeStamps | |||
-XX:+PrintGC:PrintGCTimeStamps | 可与-XX:+PrintGC -XX:+PrintGCDetails混合使用 输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs] | ||
-XX:+PrintGCApplicationStoppedTime | 打印垃圾回收期间程序暂停的时间.可与上面混合使用 | 输出形式:Total time for which application threads were stopped: 0.0468229 seconds | |
-XX:+PrintGCApplicationConcurrentTime | 打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用 | 输出形式:Application time: 0.5291524 seconds | |
-XX:+PrintHeapAtGC | 打印GC前后的详细堆栈信息 | ||
-Xloggc:filename | 把相关日志信息记录到文件以便分析. 与上面几个配合使用 | ||
-XX:+PrintClassHistogram | garbage collects before printing the histogram. | ||
-XX:+PrintTLAB | 查看TLAB空间的使用情况 | ||
XX:+PrintTenuringDistribution | 查看每次minor GC后新的存活周期的阈值 | Desired survivor size 1048576 bytes, new threshold 7 (max 15) new threshold 7即标识新的存活周期的阈值为7。 |
- 对vm分配尽可能多的memory;
- 将Xms和Xmx设为一样的值。如果虚拟机启动时设置使用的内存比较小,这个时候又需要初始化很多对象,虚拟机就必须重复地增加内存。
- 处理器核数增加,内存也跟着增大。
- 首先决定能分配给vm的最大的heap size,然后设定最佳的young generation的大小;
- 如果heap size固定后,增加young generation的大小意味着减小tenured generation大小。让tenured generation在任何时候够大,能够容纳所有live的data(留10%-20%的空余)。
- 年轻代大小选择
- 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,年轻代收集发生的频率也是最小的.同时,减少到达年老代的对象.
- 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度.因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用.
- 避免设置过小.当新生代设置过小时会导致:1.YGC次数更加频繁 2.可能导致YGC对象直接进入旧生代,如果此时旧生代满了,会触发FGC.
- 年老代大小选择
- 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象.
- 较小堆引起的碎片问题 因为年老代的并发收集器使用标记,清除算法,所以不会对堆进行压缩.当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象.但是,当堆空间较小时,运行一段时间以后,就会出现"碎片",如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记,清除方式进行回收.如果出现"碎片",可能需要进行如下配置: -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩. -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
- 用64位操作系统,Linux下64位的jdk比32位jdk要慢一些,但是吃得内存更多,吞吐量更大
- XMX和XMS设置一样大,MaxPermSize和MinPermSize设置一样大,这样可以减轻伸缩堆大小带来的压力
- 使用CMS的好处是用尽量少的新生代,经验值是128M-256M, 然后老生代利用CMS并行收集, 这样能保证系统低延迟的吞吐效率。 实际上cms的收集停顿时间非常的短,2G的内存, 大约20-80ms的应用程序停顿时间
- 系统停顿的时候可能是GC的问题也可能是程序的问题,多用jmap和jstack查看,或者killall -3 java,然后查看java控制台日志,能看出很多问题。(相关工具的使用方法将在后面的blog中介绍)
- 仔细了解自己的应用,如果用了缓存,那么年老代应该大一些,缓存的HashMap不应该无限制长,建议采用LRU算法的Map做缓存,LRUMap的最大长度也要根据实际情况设定。
- 采用并发回收时,年轻代小一点,年老代要大,因为年老大用的是并发回收,即使时间长点也不会影响其他程序继续运行,网站不会停顿
- JVM参数的设置(特别是 –Xmx –Xms –Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold等参数的设置没有一个固定的公式,需要根据PV old区实际数据 YGC次数等多方面来衡量。为了避免promotion faild可能会导致xmn设置偏小,也意味着YGC的次数会增多,处理并发访问的能力下降等问题。每个参数的调整都需要经过详细的性能测试,才能找到特定应用的最佳配置。
参考图:
四,JVM 内存参数分析实例
环境:OS:Linux version 2.6.9-79.custome.ELxenU cpu: 4 * Intel(R) Xeon(R) CPU E5410 @ 2.33GHz (双核) memory:4G
1 |
de style="line-height: 16px; background-color: inherit; font-family: Consolas, ‘Bitstream Vera Sans Mono‘, ‘Courier New‘, Courier, monospace !important; font-size: 1em !important;" >-server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=192m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70de> |
-Xmx2g 最大堆内存2G
-Xms2g 最小内存2G
-Xmn256m 新生代内存256m 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般是固定大小的(例如64m、96m),所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:PermSize=192m 持久代 192m
-Xss256k 指定线程桟大小256K
-XX:LargePageSizeInBytes=128m 指定Java heap的分页页面大小为128M
-server 可以使得新生代采用并行GC,年老代采用串行
-XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC 指定在Old Generation使用concurrent gc ,启用CMS低停顿垃圾收集器。GC线程和应用线程并行
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=70
五. 内存回收
GC:垃圾回收。回收的是堆和方法区的内存。
基本原理:找到不被使用的对象,然后回收内存。使用收集器的方式实现GC。
A)怎么找到?从根集合出发,找出无引用的对象。
根集合对象: 当前运行线程栈上引用的对象,常量及静态变量,传到本地方法且没有被本地方法释放的对象引用。
B)收集器
按回收算法为两种: 引用计数收集器,跟踪收集器。
引用计数采用算法:原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。
跟踪收集器采用算法:复制,标记-清除,标记-压缩。
按分区对待的方式分: 增量收集器(jdk5开始废弃),分代收集器。
增量收集器:就是通过一定的回收算法,把一个长时间的中断,划分为很多个小的中断,通过这种方式减少GC对用户程序的影响。
分代收集:对象存活的时间有长短,基于此将堆分为多个代,不同的代采用不同的GC方式。
按吞吐量和响应时间(暂停时间)分为: 串行收集器,并行收集器,并发收集器。
C)评估垃圾回收策略的两个重要度量
吞吐量:JVM花费在垃圾回收上的时间越长,则吞吐量越低
暂停时间:JVM垃圾回收过程当中有一个暂停期,在暂停期间,应用程序不能运行
串行收集器:单线程(单CPU)进行垃圾回收的工作。
–适用情况:数据量比较小;单处理器下并且对响应时间无要求的应用。
–缺点:只能用于小型应用
并行收集器:多个线程同时进行垃圾回收的工作。
–适用情况:”对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:科学计算。
–缺点:应用响应时间可能较长
并发收集器:传说中的CMS。垃圾回收器的一些工作与应用程序同时进行。
–适用情况:”对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器。
D)GC类型
GC有两种类型:Minor GC(Scavenge GC)和Full GC。
Minor GC:对新生代内存进行GC。
Full GC:对新生代,旧生代,持久代都进行GC。
Full GC可能的原因:
a)老年代或持久代空间满。
b)老年代采用CMS GC,GC日志出现prmotion failed和concurrent mode failure时可能触发。
prmotion failed:Minor GC是,S0(S1)放不下,放入旧生代时,仍然放不下造成的。
concurrent mode failure:CMS GC的过程中,有对象放入旧生代,此时旧生代空间不够。
c)统计得到Minor GC后存活对象放入旧生代的平均大小大于旧生代剩余空间。
d)System.gc(),只是”建议”JVM回收内存,不是强制。
六. 为何内存溢出:
既然都有GC,为什么还有内存被用尽(当然除了突然申请大空间)。这里更想说的是新生代和老年代被耗尽。
这是因为jvm有四种引用类型,不同的引用,GC的条件是不一样的。
A)四种引用
软引用:SoftReference,弱引用:WeakReference ,虚引用:PhantomReference。
软引用:内存不足,或软引用不经常使用时会被回收。适用于做缓存。
弱引用:使用弱引用创建的对象本身没有强引用,GC时一定会被回收。
虚引用:虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
除此之外都是强引用,我们一般创建一个对象时的引用就是强引用。对象被强引用,是不会不垃圾回收的。
B)内存溢出(泄露)
两种理解,
一是需要使用的对象在不断增加,直到需要分配的jvm内存超出了无法满足,于是产生溢出。
二是无用的对象在不断增加,但又无法回收,于是产生泄露。
泄露的对象有两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。这些对象不会被GC所回收,然而它却占用内存
CMSInitiatingOccupancyFraction <=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100 例如: 当xmx=128 xmn=36 SurvivorRatior=1时 CMSInitiatingOccupancyFraction<=((128.0-36)-(36-36/(1+2)))/(128-36)*100 =73.913 当xmx=128 xmn=24 SurvivorRatior=1时 CMSInitiatingOccupancyFraction<=((128.0-24)-(24-24/(1+2)))/(128-24)*100=84.615… 当xmx=3000 xmn=600 SurvivorRatior=1时 CMSInitiatingOccupancyFraction<=((3000.0-600)-(600-600/(1+2)))/(3000-600)*100=83.33 CMSInitiatingOccupancyFraction低于70% 需要调整xmn或SurvivorRatior值。
(8000-3000)-(3000-3000/3)/(8000-3000)*100=60
(4000-1500)-(1500-1500/3)/(4000-1500)*100=
60
我自己的一个配置:
$JAVA_ARGS .= " -Dresin.home=$SERVER_ROOT -server -Xmx8000M -Xms8000M -Xmn3000M -XX:PermSize=500M -XX:MaxPermSize=500M -Xss1024K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=55 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log ";
使用这个配置以后,在GC日志中没有发现任何Full GC,所以网站不会有较大的停顿,但是使用jstat -gc 发现full gc 次数还是有的,原因是CMS GC,也就是对老年代的GC,也有显示在这里的FULL GC统计次数之内,不过CMS GC是并发低停顿的,所以对网站影响非常小,特别强调,CMS不是完全没有停顿,是停顿的时间很少,原因是:
这张图表示的是CMS在执行Full GC的过程,这个过程包括了6个步骤:
# STW initial mark
# Concurrent marking
# Concurrent precleaning
# STW remark
# Concurrent sweeping
# Concurrent reset
在这六个步骤中,有两个步骤需要STW,分别是:initial mark和remark(如图所示)。而其它的四个步骤是可以和application“并发”执行,所以也就2个步骤会暂停应用服务,所以就减少了服务暂停的时间