深入JVM系列(二)之GC机制、收集器与GC调优(转)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
深⼊JVM系列(⼆)之GC机制、收集器与GC调优(转)
⼀、回顾JVM内存分配
需要了解更多内存模式与内存分配的,请看
1.1、内存分配:
1、对象优先在EDEN分配
2、⼤对象直接进⼊⽼年代
3、长期存活的对象将进⼊⽼年代
4、适龄对象也可能进⼊⽼年代:动态对象年龄判断
动态对象年龄判断:
虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升到⽼年代,当Survivor空间的相同年龄的所有对象⼤⼩总和⼤于Survivor空间的⼀半,年龄⼤于或等于该年龄的对象就可以直接进⼊⽼年代,⽆需等到MaxTenuringThreshold中指定的年龄
1.2、总结⼀下:
1、对象优先在Eden分配,这⾥⼤部分对象具有朝⽣⼣灭的特征,Minor GC主要清理该处
2、⼤对象(占内存⼤)、⽼对象(使⽤频繁)
3、Survivor⽆法容纳的对象,将进⼊⽼年代,Full GC的主要清理该处
⼆、JVM的GC机制
JVM有2个GC线程
第⼀个线程负责回收Heap的Young区
第⼆个线程在Heap不⾜时,遍历Heap,将Young 区升级为Older区
Older区的⼤⼩等于-Xmx减去-Xmn,不能将-Xms的值设的过⼤,因为第⼆个线程被迫运⾏会降低JVM的性能。
2.1、堆内存GC
JVM(采⽤分代回收的策略),⽤较⾼的频率对年轻的对象(young generation)进⾏YGC,⽽对⽼对象(tenured generation)较少(tenured generation 满了后才进⾏)进⾏Full GC。
这样就不需要每次GC都将内存中所有对象都检查⼀遍。
2.2、⾮堆内存不GC
GC不会在主程序运⾏期对PermGen Space进⾏清理,所以如果你的应⽤中有很多CLASS(特别是动态⽣成类,当然permgen space存放的内容不仅限于类)的话,就很可能出现PermGen Space错误。
2.3、内存申请、对象衰⽼过程
2.3.1、内存申请过程
1. JVM会试图为相关Java对象在Eden中初始化⼀块内存区域;
2. 当Eden空间⾜够时,内存申请结束。
否则到下⼀步;
3. JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不⾜以放⼊新对象,则试图将部分Eden中活跃对象放⼊Survivor区;
4. Survivor区被⽤来作为Eden及old的中间交换区域,当OLD区空间⾜够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
5. 当old区空间不够时,JVM会在old区进⾏major collection;
6. 完全垃圾收集后,若Survivor及old区仍然⽆法存放从Eden复制过来的部分对象,导致JVM⽆法在Eden区为新对象创建内存区域,则出现"Out of memory错误";
2.3.2、对象衰⽼过程
1. 新创建的对象的内存都分配⾃eden。
Minor collection的过程就是将eden和在⽤survivor space中的活对象copy到空闲survivor space中。
对象在young generation⾥经历了
⼀定次数(可以通过参数配置)的minor collection后,就会被移到old generation中,称为tenuring。
GC触发条件
GC 类型触发条件触发时发⽣了什么注意
查看
⽅式
YGC eden空间不⾜清空Eden+from survivor中所
有no ref的对象占⽤的内存
将eden+from sur中所有存活
的对象copy到to sur中
⼀些对象将晋升到old中:
to sur放不下的
存活次数超过turning
threshold中的
重新计算tenuring
threshold(serial parallel GC
会触发此项)
重新调整Eden 和from的⼤⼩
(parallel GC会触发此项)
全过程暂停应
⽤
是否为多线程
处理由具体的
GC决定
jstat –
gcutil
gc
log
FGC old空间不⾜
perm空间不⾜
显⽰调⽤
System.GC, RMI
等的定时触发
YGC时的悲观策略
dump live的内存
信息时(jmap –
dump:live)
清空heap中no ref的对象
permgen中已经被卸载的
classloader中加载的class信
息
如配置了CollectGenOFirst,
则先触发YGC(针对serial
GC)
如配置了
ScavengeBeforeFullGC,则
先触发YGC(针对serial GC)
全过程暂停应
⽤
是否为多线程
处理由具体的
GC决定
是否压缩需要
看配置的具体
GC
jstat –
gcutil
gc
log
permanent generation空间不⾜会引发Full GC,仍然不够会引发PermGen Space错误。
三、GC监视、收集器与GC调优
3.1、监视JVM GC
⾸先说⼀下如何监视JVM GC,可以⽤JDK中的jstat⼯具,也可以在程序启动的opt⾥加上如下⼏个参数(注:这两个参数只针对SUN的HotSpotVM):
1. 1 -XX:-PrintGCPrintmessagesatgarbagecollection.Manageable.
2 -XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.)
3 -XX:-PrintGCTimeStampsPrinttimestampsatgarbagecollection.Manageable(Introducedin1.4.0.)
当把-XX:-PrintGCDetails加⼊到javaopt⾥以后可以看见如下输出:
[GC[DefNew:34538K->2311K(36352K),0.0232439secs]45898K->15874K(520320K),0.0233874secs]
[FullGC[Tenured:13563K->15402K(483968K),0.2368177secs]21163K->15402K(520320K),[Perm:28671K->28635K(28672K)],0.2371537secs]
他们分别显⽰了JVM GC的过程,清理出了多少空间。
第⼀⾏GC使⽤的是‘普通GC’(MinorCollections),第⼆⾏使⽤的是‘全GC’(MajorCollections)。
他们的区别很⼤,在第⼀⾏最后我们可以看见他的时间是0.0233874秒,⽽第⼆⾏的FullGC的时间是0.2371537秒。
第⼆⾏的时间是第⼀⾏的接近10倍,也就是我们这次调优的重点,减少FullGC的次数,以为FullGC会暂停程序⽐较长的时间,如果FullGC的次数⽐较多。
程序就会经常性的假死。
注:
GC信息的格式
[GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs] <collector> GC为minor收集过程中使⽤的垃圾收集器起的内部名称.
<starting occupancy1> young generation 在进⾏垃圾收集前被对象使⽤的存储空间.
<ending occupancy1> young generation 在进⾏垃圾收集后被对象使⽤的存储空间
<pause time1> minor收集使应⽤暂停的时间长短(秒)
<starting occupancy3> 整个堆(Heap Size)在进⾏垃圾收集前被对象使⽤的存储空间
<ending occupancy3> 整个堆(Heap Size)在进⾏垃圾收集后被对象使⽤的存储空间
<pause time3> 整个垃圾收集使应⽤暂停的时间长短(秒),包括major收集使应⽤暂停的时间(如果发⽣了major收集).
GC信息的选项
-XX:+PrintGCDetails 显⽰GC的详细信息
-XX:+PrintGCApplicationConcurrentTime 打印应⽤执⾏的时间
-XX:+PrintGCApplicationStoppedTime 打印应⽤被暂停的时间
3.2、collector收集器的种类
GC在 HotSpot VM 5.0⾥有四种:
incremental (sometimes called train) low pause collector已被废弃,不在介绍.
类别serial collector
parallel collector
( throughput collector )
concurrent collector
(concurrent low pause collector)
介绍单线程收集器
使⽤单线程去完成所有的gc⼯
作,没有线程间的通信,这种
⽅式会相对⾼效
并⾏收集器
使⽤多线程的⽅式,利⽤多
CUP来提⾼GC的效率
主要以到达⼀定的吞吐量为
⽬标
并发收集器
使⽤多线程的⽅式,利⽤多CUP来提⾼GC
的效率
并发完成⼤部分⼯作,使得gc pause短
试
⽤场景单处理器机器且没有pause
time的要求
适⽤于科学技术和后台处理
有中规模/⼤规模数据集⼤⼩
的应⽤且运⾏在多处理器
上,关注吞吐量
(throughput)
适合中规模/⼤规模数据集⼤⼩的应⽤,应
⽤服务器,电信领域
关注response time,⽽不是throughput
使⽤Client模式下默认
可使⽤
可⽤-XX:+UseSerialGC强制使
⽤
优点:对server应⽤没什么优点
缺点:慢,不能充分发挥硬件资源
Server模式下默认
--YGC:PS FGC:Parallel
MSC
可⽤-XX:+UseParallelGC
或-XX:+UseParallelOldGC
强制指定
--ParallelGC代表FGC为
Parallel MSC
--ParallelOldGC代表FGC为
Parallel Compacting
优点:⾼效
缺点:当heap变⼤后,造成的
暂停时间会变得⽐较长
可⽤-XX:+UseConcMarkSweepGC强制指
定
优点:
对old进⾏回收时,对应⽤造成的暂停时间⾮
常端,适合对latency要求⽐较⾼的应⽤
缺点:
1.内存碎⽚和浮动垃圾
2.old去的内存分配效率低
3.回收的整个耗时⽐较长
4.和应⽤争抢CPU
内存回收触发YGC
eden空间不⾜
FGC
old空间不⾜
perm空间不⾜
显⽰调⽤System.gc() ,包括RMI
等的定时触发
dump live的内存信息时(jmap –
dump:live)
YGC
eden空间不⾜
FGC
old空间不⾜
perm空间不⾜
显⽰调⽤System.gc() ,包括
RMI等的定时触发
--YGC前&YGC后
dump live的内存信息时
(jmap –dump:live)
YGC
eden空间不⾜
CMS GC
1.old Gen的使⽤率⼤的⼀定的⽐率默认为
92%
2.配置了CMSClassUnloadingEnabled,且
Perm Gen的使⽤达到⼀定的⽐率默认为
92%
3.Hotspot⾃⼰根据估计决定是否要触法
4.在配置了ExplictGCInvokesConcurrent的
情况下显⽰调⽤了System.gc.
Full GC(Serial MSC)
promotion failed 或 concurrent Mode
Failure时;
内
存回收触发时发⽣了什么YGC
eden空间不⾜
FGC
old空间不⾜
perm空间不⾜
显⽰调⽤System.gc() ,包括RMI
等的定时触发
dump live的内存信息时(jmap –
dump:live)
YGC
同serial动作基本相同,不同
点:
1.多线程处理
2.YGC的最后不仅重新计算
Tenuring Threshold,还会重
新调整Eden和From的⼤⼩
FGC
1.如配置了
ScavengeBeforeFullGC(默
认),则先触发YGC(??)
2.MSC:清空heap中的no ref
对象,permgen中已经被卸载
的classloader中加载的
class信息,并进⾏压缩
pacting:清空heap中
部分no ref的对象,permgen
中已经被卸载的classloader
中加载的class信息,并进⾏
YGC
同serial动作基本相同,不同点:
1.多线程处理
CMSGC:
1.old gen到达⽐率时只清除old gen中no
ref的对象所占⽤的空间
2.perm gen到达⽐率时只清除已被清除的
classloader加载的class信息
FGC
同serial
部分压缩
可⽤-XX:+UseSerialGC强制使⽤
-XX:SurvivorRatio=x,控制eden/s0/s1的⼤⼩
-XX:MaxTenuringThreshold,⽤于控制对象在新⽣代存活的最⼤次数
-
XX:PretenureSizeThreshold=x,控制超过多⼤的字节的对象就在old分配.eden/s0/s1的⼤⼩
-
XX:MaxTenuringThreshold,
⽤于控制对象在新⽣代存活
的最⼤次数
-
XX:UseAdaptiveSizePolicy
去掉YGC后动态调整eden
from已经tenuringthreshold
的动作
-XX:ParallelGCThreads 设
置并⾏的线程数
设置
使⽤到达多少⽐率时触发
Perm Gen使⽤到达多少⽐率时触发
禁
hostspot⾃⾏触发CMS GC
注:
throughput collector与concurrent low pause collector的区别是throughput collector只在young area使⽤使⽤多线程,⽽concurrent low pause collector则在tenured
generation也使⽤多线程。
根据官⽅⽂档,他们俩个需要在多CPU的情况下,才能发挥作⽤。
在⼀个CPU的情况下,会不如默认的serial collector,因为线程管理需要耗费CPU资源。
⽽在两个CPU
的情况下,也提⾼不⼤。
只是在更多CPU的情况下,才会有所提⾼。
当然 concurrent low pause collector有⼀种模式可以在CPU较少的机器上,提供尽可能少的停顿的模
式,见CMS GC Incremental mode。
当要使⽤throughput collector时,在java opt⾥加上-XX:+UseParallelGC,启动throughput collector收集。
也可加上-XX:ParallelGCThreads=<desired number>来改变线程
数。
还有两个参数 -XX:MaxGCPauseMillis=<nnn>和 -XX:GCTimeRatio=<nnn>,MaxGCPauseMillis=<nnn>⽤来控制最⼤暂停时间,⽽-XX: GCTimeRatio可以提⾼GC说
占CPU的⽐,以最⼤话的减⼩heap。
附注SUN的官⽅说明:
1. 1. The throughput collector: this collector uses a parallel version of the young generation collector. It is used if the -XX:+UseParallelGC option is passed on the command line. The tenured generation collector is the same as the serial colle
2. The concurrent low pause collector: this collector is used if the -Xincgc™ or -XX:+UseConcMarkSweepGC is passed on the command line. The concurrent collector is used to collect the tenured generation and does most of the collectio
3. The incremental (sometimes called train) low pause collector: this collector is used only if -XX:+UseTrainGC is passed on the command line. This collector has not changed since the J2SE Platform version 1.
4.2 and is currently not und
CMS GC Incremental mode
当要使⽤ concurrent low pause collector时,在java的opt⾥加上 -XX:+UseConcMarkSweepGC。
concurrent low pause collector还有⼀种为CPU少的机器准备的模式,叫
Incremental mode。
这种模式使⽤⼀个CPU来在程序运⾏的过程中GC,只⽤很少的时间暂停程序,检查对象存活。
在Incremental mode⾥,每个收集过程中,会暂停两次,第⼆次略长。
第⼀次⽤来,简单从root查询存活对象。
第⼆次⽤来,详细检查存活对象。
整个过程如下:
1. 1 * stop all application threads; do the initial mark; resume all application threads(第⼀次暂停,初始话标记)
2 * do the concurrent mark (uses one procesor for the concurrent work)(运⾏是标记)
3 * do the concurrent pre-clean (uses one processor for the concurrent work)(准备清理)
4 * stop all application threads; do the remark; resume all application threads(第⼆次暂停,标记,检查)
5 * do the concurrent sweep (uses one processor for the concurrent work)(运⾏过程中清理)
6 * do the concurrent reset (uses one processor for the concurrent work)(复原)
当要使⽤Incremental mode时,需要使⽤以下⼏个变量:
1. 1 -XX:+CMSIncrementalMode default: disabled 启动i-CMS模式(must with -XX:+UseConcMarkSweepGC)
2 -XX:+CMSIncrementalPacing default: disabled 提供⾃动校正功能
3 -XX:CMSIncrementalDutyCycle=<N> default: 50 启动CMS的上线
4 -XX:CMSIncrementalDutyCycleMin=<N> default: 10 启动CMS的下线
5 -XX:CMSIncrementalSafetyFactor=<N> default: 10 ⽤来计算循环次数
6 -XX:CMSIncrementalOffset=<N> default: 0 最⼩循环次数(This is the percentage (0-100) by which the incremental mode duty cycle is shifted to the right within the period between minor collections.)
7 -XX:CMSExpAvgFactor=<N> default: 25 提供⼀个指导收集数
SUN推荐的使⽤参数是:
1. 1 -XX:+UseConcMarkSweepGC \
2 -XX:+CMSIncrementalMode \
3 -XX:+CMSIncrementalPacing \
4 -XX:CMSIncrementalDutyCycleMin=0 \
5 -XX:CMSIncrementalDutyCycle=10 \
6 -XX:+PrintGC Details \
7 -XX:+PrintGCTimeStamps \
8 -XX:-TraceClassUnloading
注:如果使⽤throughput collector和concurrent low pause collector,这两种垃圾收集器,需要适当的挺⾼内存⼤⼩,以为多线程做准备。
3.3、如何选择collector
app运⾏在单处理器机器上且没有pause time的要求,让vm选择UseSerialGC.
重点考虑peak application performance(⾼性能),没有pause time太严格要求,让vm选择或者UseParallelGC+UseParallelOldGC(optionally).
重点考虑response time,pause time要⼩,UseConcMarkSweepGC.
Garbage Collctor – Future
1. 1 Garbage First(G1)
2 jdk1.6 update 14 or jdk7
3 few flags need to set
4 -XX:MaxGCPauseMillis=100
5 -XX:GCPauseIntervalMillis=6000
还没尝试过使⽤…
Summary
1. 1import java.util.ArrayList;
2import java.util.List;
3public class SummaryCase {
4public static void main(String[] args) throws InterruptedException {
5 List<Object> caches = new ArrayList<Object>();
6for (int i = 0; i < 7; i++) {
7 caches.add(new byte[1024 * 1024 * 3]);
8 Thread.sleep(1000);
9 }
10 caches.clear();
11for (int i = 0; i < 2; i++) {
12 caches.add(new byte[1024 * 1024 * 3]);
13 Thread.sleep(1000);
14 }
15 }
16 }
}
⽤以下两种参数执⾏,会执⾏⼏次YGC⼏次FGC?
-Xms30M -Xmx30M -Xmn10M -Xloggc:gc.log -XX:+PrintTenuringDistribution -XX:+UseParallelGC
1. 1
2.062: [GC
2 Desired survivor size 1310720 bytes, new threshold 7 (max 15)
3 6467K->6312K(29440K), 0.003821
4 secs]
4 4.066: [GC
5 Desired survivor size 1310720 bytes, new threshold 7 (max 15)
6 12536K->12440K(29440K), 0.0036804 secs]
7 6.070: [GC
8 Desired survivor size 1310720 bytes, new threshold 7 (max 15)
9 18637K->18584K(29440K), 0.0040175 secs]
10 6.074: [Full GC 18584K->18570K(29440K), 0.0031329 secs]
11 8.078: [Full GC 24749K->3210K(29440K), 0.0045590 secs]
(具体分析见)
-Xms30M -Xmx30M -Xmn10M -Xloggc:gc.log -XX:+PrintTenuringDistribution -XX:+UseSerialGC
1. 1
2.047: [GC
2 Desired survivor size 524288 bytes, new threshold 15 (max 15)
3 - age 1: 14202
4 bytes, 142024 total
4 6472K->6282K(29696K), 0.0048686 secs]
5 4.053: [GC
6 Desired survivor size 524288 bytes, new threshold 15 (max 15)
7 - age 2: 141880 bytes, 141880 total
8 12512K->12426K(29696K), 0.0047334 secs]
9 6.058: [GC
10 Desired survivor size 524288 bytes, new threshold 15 (max 15)
11 - age 3: 141880 bytes, 141880 total
12 18627K->18570K(29696K), 0.0049135 secs]
13 8.063: [Full GC 24752K->3210K(29696K), 0.0077895 secs]
(具体分析见)
四、GC调优的⼩例⼦
例1:Heap size 设置
JVM 堆的设置是指java程序运⾏过程中JVM可以调配使⽤的内存空间的设置.JVM在启动的时候会⾃动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最⼤空间(-Xmx)是物理内存的1/4。
可以利⽤JVM提供的-Xmn -Xms -Xmx等选项可进⾏设置。
Heap size 的⼤⼩是Young Generation 和Tenured Generaion 之和。
当在JAVA_HOME下demo/jfc/SwingSet2/⽬录下执⾏下⾯的命令。
1. 1 java -jar -Xmn4m -Xms16m -Xmx16m SwingSet
2.jar
系统输出为:
1. 1 Exception in thread "Image Fetcher 0" ng.OutOfMemoryError: Java heap space
2 Exception in thread "Image Fetcher 3" ng.OutOfMemoryError: Java heap space
3 Exception in thread "Image Fetcher 1" ng.OutOfMemoryError: Java heap space
4 Exception in thread "Image Fetcher 2" ng.OutOfMemoryError: Java heap space
除了这些异常信息外,还会发现程序的响应速度变慢了。
这说明Heap size 设置偏⼩,GC占⽤了更多的时间,⽽应⽤分配到的执⾏时间较少。
提⽰:在JVM中如果98%的时间是⽤于GC且可⽤的Heap size 不⾜2%的时候将抛出此异常信息。
将上⾯的命令换成以下命令执⾏则应⽤能够正常使⽤,且未抛出任何异常。
java -jar -Xmn4m -Xms16m -Xmx32m SwingSet2.jar
提⽰:Heap Size 最⼤不要超过可⽤物理内存的80%,⼀般的要将-Xms和-Xmx选项设置为相同,⽽-Xmn为1/4的-Xmx值。
例2:Young Generation(-Xmn)的设置
在本例中看⼀下Young Generation的设置不同将有什么现象发⽣。
假设将Young generation 的⼤⼩设置为4M ,即执⾏java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar,
屏幕输出如下(节选)
1. 1 [GC [DefNew: 3968K->64K(4032K), 0.0923407 secs] 3968K->2025K(32704K), 0.0931870 secs]
2 [GC [DefNew: 4021K->64K(4032K), 0.0356847 secs] 5983K->2347K(32704K), 0.0365441 secs]
3 [GC [DefNew: 3995K->39K(4032K), 0.0090603 secs] 6279K->2372K(32704K), 0.0093377 secs]
将程序体制并将Young Generation的⼤⼩设置为8M,即执⾏
1. 1 java -jar -verbose:gc -Xmn8m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet
2.jar
屏幕输出如下(节选)
1. 1 [GC [DefNew: 7808K->192K(8000K), 0.1016784 secs] 7808K->2357K(32576K), 0.1022834 secs]
2 [GC [DefNew: 8000K->70K(8000K), 0.0149659 secs] 10165K->2413K(32576K), 0.0152557 secs]
3 [GC [DefNew: 7853K->59K(8000K), 0.0069122 secs] 10196K->2403K(32576K), 0.0071843 secs]
4 [GC [DefNew: 7867K->171K(8000K), 0.007574
5 secs] 10211K->2681K(32576K), 0.007837
6 secs]
5 [GC [DefNew: 7970K->192K(8000K), 0.0201353 secs] 10480K->2923K(32576K), 0.0206867 secs]
6 [GC [DefNew: 7979K->30K(8000K), 0.1787079 secs] 10735K->4824K(32576K), 0.1790065 secs]
那么根据GC输出的信息(这⾥取第⼀⾏)做⼀下Minor收集的⽐较。
可以看出两次的Minor收集分别在Young generation中找回3904K(3968K->64K)和7616K(7808K->192K)⽽对于整个jvm则找回 1943K(3968K->2025)和5451K(7808K->2357K)。
第⼀种情况下Minor收集了⼤约50%(1943/3904)的对象,⽽另外的50%的对象则被移到了tenured generation。
在第⼆中情况下Minor收集了⼤约72%的对象,只有不到30%的对象被移到了Tenured Generation.这个例⼦说明此应⽤在的Young generation 设置为4m时显的偏⼩。
提⽰:⼀般的Young Generation的⼤⼩是整个Heap size的1/4。
Young generation的minor收集率应⼀般在70%以上。
当然在实际的应⽤中需要根据具体情况进⾏调整。
例3:Young Generation对应⽤响应的影响
还是使⽤-Xmn4m 和-Xmn8m进⾏⽐较,先执⾏下⾯的命令
1. 1 java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime SwingSet
2.jar
屏幕输出如下(节选)
1. 1 Application time: 0.5114944 seconds
2 [GC [DefNew: 3968K->64K(4032K), 0.0823952 secs] 3968K->2023K(32704K), 0.0827626 secs]
3 Total time for which application threads were stopped: 0.0839428 seconds
4 Application time: 0.9871271 seconds
5 [GC [DefNew: 4020K->64K(4032K), 0.0412448 secs] 5979K->2374K(32704K), 0.0415248 secs]
6 Total time for which application threads were stopped: 0.0464380 seconds
Young Generation 的Minor收集占⽤的时间可以计算如下:
应⽤线程被中断的总时常/(应⽤执⾏总时?L+应⽤线程被中断的总时常),那么在本例中垃圾收集占⽤的时?L约为系统的5%~14%。
那么当垃圾收集占⽤的时间的⽐例越⼤的时候,系统的响应将越慢。
提⽰:对于互联⽹应⽤系统的响应稍微慢⼀些,⽤户是可以接受的,但是对于GUI类型的应⽤响应速度慢将会给⽤户带来⾮常不好的体验。
例4:如何决定Tenured Generation 的⼤⼩
分别以-Xmn8m -Xmx32m和-Xmn8m -Xmx64m进⾏对⽐,先执⾏
1. 1 java -verbose:gc -Xmn8m -Xmx32m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类
命令⾏将提⽰(只提取了Major收集)
1. 1 111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]
2 122.463: [GC 122.463: [DefNew: 8128K->8128K(8128K), 0.0000560 secs]122.463: [Tenured: 18630K->2366K(24576K), 0.1322560 secs] 26758K->2366K(32704K), 0.1325284 secs]
3 133.896: [GC 133.897: [DefNew: 8128K->8128K(8128K), 0.0000443 secs]133.897: [Tenured: 18240K->2573K(24576K), 0.1340199 secs] 26368K->2573K(32704K), 0.1343218 secs]
4 144.112: [GC 144.112: [DefNew: 8128K->8128K(8128K), 0.0000544 secs]144.112: [Tenured: 16564K->2304K(24576K), 0.1246831 secs] 24692K->2304K(32704K), 0.1249602 secs]
再执⾏
1. 1 java -verbose:gc -Xmn8m -Xmx64m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类
命令⾏将提⽰(只提取了Major收集)
1. 1 90.597: [GC 90.597: [DefNew: 8128K->8128K(8128K), 0.0000542 secs]90.597: [Tenured: 49841K->5141K(57344K), 0.2129882 secs] 57969K->5141K(65472K), 0.2133274 secs]
2 120.899: [GC 120.899: [DefNew: 8128K->8128K(8128K), 0.0000550 secs]120.899: [Tenured: 50384K->2430K(57344K), 0.2216590 secs] 58512K->2430K(65472K), 0.2219384 secs]
3 153.968: [GC 153.968: [DefNew: 8128K->8128K(8128K), 0.0000511 secs]153.968: [Tenured: 51164K->2309K(57344K), 0.2193906 secs] 59292K->2309K(65472K), 0.2196372 secs]
可以看出在Heap size 为32m的时候系统等候时间约为0.13秒左右,⽽设置为64m的时候等候时间则增⼤到0.22秒左右了。
但是在32m的时候系统的Major收集间隔为 10秒左右,⽽Heap size 增加到64m的时候为30秒。
那么应⽤在运⾏的时候是选择32m还是64m呢?如果应⽤是web类型(即要求有⼤的吞吐量)的应⽤则使⽤64m(即 heapsize⼤⼀些)的⽐较好。
对于要求实时响应要求较⾼的场合(例如GUI型的应⽤)则使⽤32m⽐较好⼀些。
注意:
1。
因为在JVM5运⾏时已经对Heap-size进⾏了优化,所以在能确定java应⽤运⾏时不会超过默认的Heap size的情况下建议不要对这些值进⾏修改。
2。
Heap size的 -Xms -Xmn 设置不要超出物理内存的⼤⼩。
否则会提⽰“Error occurred during initialization of VM Could not reserve enough space for object heap”。
例5:如何缩短minor收集的时间
下⾯⽐较⼀下采⽤-XX:+UseParNewGC选项和不采⽤它的时候的minor收集将有什么不同。
先执⾏
1. 1 java -jar -server -verbose:gc -Xmn8m -Xms32m -Xmx32m SwingSet
2.jar
系统将输出如下信息(⽚段〕
1. 1 [GC 7807K->2641K(32576K), 0.0676654 secs]
2 [GC 10436K->3108K(32576K), 0.0245328 secs]
3 [GC 10913K->3176K(32576K), 0.0072865 secs]
4 [GC 10905K->4097K(32576K), 0.0223928 secs]
之后再执⾏
1. 1 java -jar -server -verbose:gc -XX:+UseParNewGC -Xmn8m -Xms32m -Xmx32m SwingSet
2.jar
系统将输出如下信息(⽚段〕
1. 1 [ParNew 7808K->2656K(32576K), 0.0447687 secs]
2 [ParNew 10441K->3143K(32576K), 0.0179422 secs]
3 [ParNew 10951K->3177K(32576K), 0.003191
4 secs]
4 [ParNew 10985K->3867K(32576K), 0.0154991 secs]
很显然使⽤了-XX:+UseParNewGC选项的minor收集的时间要⽐不使⽤的时候优。
例6:如何缩短major收集的时间
下⾯⽐较⼀下采⽤-XX:+UseConcMarkSweepGC选项和不采⽤它的时候的major收集将有什么不同。
先执⾏
1. 1 java -jar -verbose:gc -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet
2.jar
系统将输出如下信息(⽚段〕
1. 1 [Full GC 22972K->18690K(262080K), 0.2326676 secs]
2 [Full GC 18690K->18690K(262080K), 0.1701866 secs
之后再执⾏
1. 1 java -jar -verbose:gc -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet
2.jar 系统将输出如下信息(⽚段〕
1. 1 [Full GC 56048K->18869K(260224K), 0.3104852 secs]
提⽰:此选项在Heap Size ⽐较⼤⽽且Major收集时间较长的情况下使⽤更合适。
例7:关于-server选项。