`

转jvm

    博客分类:
  • java
jvm 
阅读更多

一、回顾JVM内存分配

 

需要了解更多内存模式与内存分配的,请看 深入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工具,也可以在java程序启动的opt里加上如下几个参数(注:这两个参数只针对SUN的HotSpotVM):

 

 

[java] view plaincopy
 
  1. -XX:-PrintGCPrintmessagesatgarbagecollection.Manageable.    
  2. -XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.)    
  3. -XX:-PrintGCTimeStampsPrinttimestampsatgarbagecollection.Manageable(Introducedin1.4.0.)   
[java] view plaincopy
 
  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等的定时触发
YGC时的悲观策略
dump live的内存信息时(jmap –dump:live)
YGC
eden空间不足
FGC
old空间不足
perm空间不足
显示调用System.gc() ,包括RMI等的定时触发
YGC时的悲观策略--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等的定时触发
YGC时的悲观策略
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信息,并进行压缩
3.Compacting:清空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分配.
-XX:SurvivorRatio=x,控制eden/s0/s1的大小
-XX:MaxTenuringThreshold,用于控制对象在新生代存活的最大次数

-XX:UseAdaptiveSizePolicy 去掉YGC后动态调整eden from已经tenuringthreshold的动作

-XX:ParallelGCThreads 设置并行的线程数

-XX:CMSInitiatingOccupancyFraction 设置old gen使用到达多少比率时触发
-XX:CMSInitiatingPermOccupancyFraction,设置Perm Gen使用到达多少比率时触发
-XX:+UseCMSInitiatingOccupancyOnly禁止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的官方说明: 

 

[plain] view plaincopy
 
  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 collector.  
  2.   
  3. 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 collection concurrently with the execution of the application. The application is paused for short periods during the collection. A parallel version of the young generation copying collector is used with the concurrent collector. The concurrent low pause collector is used if the option -XX:+UseConcMarkSweepGC is passed on the command line.  
  4.   
  5. 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 under active development. It will not be supported in future releases. Please see the 1.4.2 GC Tuning Document for information on this collector.  
[plain] view plaincopy
 
  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 collector.  
  2.   
  3. 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 collection concurrently with the execution of the application. The application is paused for short periods during the collection. A parallel version of the young generation copying collector is used with the concurrent collector. The concurrent low pause collector is used if the option -XX:+UseConcMarkSweepGC is passed on the command line.  
  4.   
  5. 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 under active development. It will not be supported in future releases. Please see the 1.4.2 GC Tuning Document for information on this collector.  



 

CMS GC Incremental mode

 

       当要使用 concurrent low pause collector时,在java的opt里加上 -XX:+UseConcMarkSweepGC。concurrent low pause collector还有一种为CPU少的机器准备的模式,叫Incremental mode。这种模式使用一个CPU来在程序运行的过程中GC,只用很少的时间暂停程序,检查对象存活。

       在Incremental mode里,每个收集过程中,会暂停两次,第二次略长。第一次用来,简单从root查询存活对象。第二次用来,详细检查存活对象。整个过程如下:

 

[java] view plaincopy
 
  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)(复原)  
[java] view plaincopy
 
  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时,需要使用以下几个变量:

 

[java] view plaincopy
 
  1. -XX:+CMSIncrementalMode default: disabled 启动i-CMS模式(must with -XX:+UseConcMarkSweepGC)  
  2. -XX:+CMSIncrementalPacing default: disabled 提供自动校正功能  
  3. -XX:CMSIncrementalDutyCycle=<N> default50 启动CMS的上线  
  4. -XX:CMSIncrementalDutyCycleMin=<N> default10 启动CMS的下线  
  5. -XX:CMSIncrementalSafetyFactor=<N> default10 用来计算循环次数  
  6. -XX:CMSIncrementalOffset=<N> default0 最小循环次数(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> default25 提供一个指导收集数  
[java] view plaincopy
 
  1. -XX:+CMSIncrementalMode default: disabled 启动i-CMS模式(must with -XX:+UseConcMarkSweepGC)  
  2. -XX:+CMSIncrementalPacing default: disabled 提供自动校正功能  
  3. -XX:CMSIncrementalDutyCycle=<N> default50 启动CMS的上线  
  4. -XX:CMSIncrementalDutyCycleMin=<N> default10 启动CMS的下线  
  5. -XX:CMSIncrementalSafetyFactor=<N> default10 用来计算循环次数  
  6. -XX:CMSIncrementalOffset=<N> default0 最小循环次数(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> default25 提供一个指导收集数  



 

 
     SUN推荐的使用参数是:
[java] view plaincopy
 
  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  
[java] view plaincopy
 
  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

 

[java] view plaincopy
 
  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  
[java] view plaincopy
 
  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

[java] view plaincopy
 
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. public class SummaryCase {  
  4.     public static void main(String[] args) throws InterruptedException {  
  5.         List<Object> caches = new ArrayList<Object>();  
  6.         for (int i = 0; i < 7; i++) {  
  7.             caches.add(new byte[1024 * 1024 * 3]);  
  8.             Thread.sleep(1000);  
  9.         }  
  10.         caches.clear();  
  11.         for (int i = 0; i < 2; i++) {  
  12.             caches.add(new byte[1024 * 1024 * 3]);  
  13.             Thread.sleep(1000);  
  14.         }  
  15.     }  
  16. }  
[java] view plaincopy
 
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. public class SummaryCase {  
  4.     public static void main(String[] args) throws InterruptedException {  
  5.         List<Object> caches = new ArrayList<Object>();  
  6.         for (int i = 0; i < 7; i++) {  
  7.             caches.add(new byte[1024 * 1024 * 3]);  
  8.             Thread.sleep(1000);  
  9.         }  
  10.         caches.clear();  
  11.         for (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
 
[java] view plaincopy
 
  1. 2.062: [GC  
  2. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  3.  6467K->6312K(29440K), 0.0038214 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]  
[java] view plaincopy
 
  1. 2.062: [GC  
  2. Desired survivor size 1310720 bytes, new threshold 7 (max 15)  
  3.  6467K->6312K(29440K), 0.0038214 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]  

(具体分析见http://rdc.taobao.com/team/jm/archives/440)
 
-Xms30M -Xmx30M -Xmn10M  -Xloggc:gc.log -XX:+PrintTenuringDistribution -XX:+UseSerialGC
[java] view plaincopy
 
  1. 2.047: [GC  
  2. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  3. - age   1:     142024 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]  
[java] view plaincopy
 
  1. 2.047: [GC  
  2. Desired survivor size 524288 bytes, new threshold 15 (max 15)  
  3. - age   1:     142024 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]  
(具体分析见http://rdc.taobao.com/team/jm/archives/458)


四、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/目录下执行下面的命令。

 

[java] view plaincopy
 
  1. java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar  
[java] view plaincopy
 
  1. java -jar -Xmn4m -Xms16m -Xmx16m SwingSet2.jar  



系统输出为:

[java] view plaincopy
 
  1. Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space  
  2. Exception in thread "Image Fetcher 3" java.lang.OutOfMemoryError: Java heap space  
  3. Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space  
  4. Exception in thread "Image Fetcher 2" java.lang.OutOfMemoryError: Java heap space  
[java] view plaincopy
 
  1. Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space  
  2. Exception in thread "Image Fetcher 3" java.lang.OutOfMemoryError: Java heap space  
  3. Exception in thread "Image Fetcher 1" java.lang.OutOfMemoryError: Java heap space  
  4. Exception in thread "Image Fetcher 2" java.lang.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,

 

 

屏幕输出如下(节选)

 

[java] view plaincopy
 
  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]  
[java] view plaincopy
 
  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,即执行

 

 

[java] view plaincopy
 
  1. java -jar -verbose:gc -Xmn8m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar  
[java] view plaincopy
 
  1. java -jar -verbose:gc -Xmn8m -Xms32m -Xmx32m -XX:+PrintGCDetails SwingSet2.jar  



 

屏幕输出如下(节选)

[java] view plaincopy
 
  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.0075745 secs] 10211K->2681K(32576K), 0.0078376 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]  
[java] view plaincopy
 
  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.0075745 secs] 10211K->2681K(32576K), 0.0078376 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进行比较,先执行下面的命令

 

 

[java] view plaincopy
 
  1. java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime SwingSet2.jar  
[java] view plaincopy
 
  1. java -jar -verbose:gc -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime SwingSet2.jar  



屏幕输出如下(节选)

[java] view plaincopy
 
  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  
[java] view plaincopy
 
  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进行对比,先执行

 

 

[java] view plaincopy
 
  1. java -verbose:gc -Xmn8m -Xmx32m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类  
[java] view plaincopy
 
  1. java -verbose:gc -Xmn8m -Xmx32m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类  



命令行将提示(只提取了Major收集)

 

 

[java] view plaincopy
 
  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]  
[java] view plaincopy
 
  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]  



再执行

 

 

[java] view plaincopy
 
  1. java -verbose:gc -Xmn8m -Xmx64m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类  
[java] view plaincopy
 
  1. java -verbose:gc -Xmn8m -Xmx64m-XX:+PririntGCDetails -XX:+PrintGCTimeStamps java类  



 

命令行将提示(只提取了Major收集)

[java] view plaincopy
 
  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]  
[java] view plaincopy
 
  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收集将有什么不同。先执行

[java] view plaincopy
 
  1. java -jar -server -verbose:gc -Xmn8m -Xms32m -Xmx32m SwingSet2.jar   
[java] view plaincopy
 
  1. java -jar -server -verbose:gc -Xmn8m -Xms32m -Xmx32m SwingSet2.jar   



系统将输出如下信息(片段〕

[java] view plaincopy
 
  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]  
[java] view plaincopy
 
  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]  



之后再执行 

 

 

[java] view plaincopy
 
  1. java -jar -server -verbose:gc -XX:+UseParNewGC -Xmn8m -Xms32m -Xmx32m SwingSet2.jar  
[java] view plaincopy
 
  1. java -jar -server -verbose:gc -XX:+UseParNewGC -Xmn8m -Xms32m -Xmx32m SwingSet2.jar  



系统将输出如下信息(片段〕

[java] view plaincopy
 
  1. [ParNew 7808K->2656K(32576K), 0.0447687 secs]  
  2. [ParNew 10441K->3143K(32576K), 0.0179422 secs]  
  3. [ParNew 10951K->3177K(32576K), 0.0031914 secs]  
  4. [ParNew 10985K->3867K(32576K), 0.0154991 secs]  
[java] view plaincopy
 
  1. [ParNew 7808K->2656K(32576K), 0.0447687 secs]  
  2. [ParNew 10441K->3143K(32576K), 0.0179422 secs]  
  3. [ParNew 10951K->3177K(32576K), 0.0031914 secs]  
  4. [ParNew 10985K->3867K(32576K), 0.0154991 secs]  



很显然使用了-XX:+UseParNewGC选项的minor收集的时间要比不使用的时候优。

 

 

 

例6:如何缩短major收集的时间 

 

下面比较一下采用-XX:+UseConcMarkSweepGC选项和不采用它的时候的major收集将有什么不同。先执行

[java] view plaincopy
 
  1. java -jar -verbose:gc -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  
[java] view plaincopy
 
  1. java -jar -verbose:gc -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  



系统将输出如下信息(片段〕

[java] view plaincopy
 
  1. [Full GC 22972K->18690K(262080K), 0.2326676 secs]  
  2. [Full GC 18690K->18690K(262080K), 0.1701866 secs  
[java] view plaincopy
 
  1. [Full GC 22972K->18690K(262080K), 0.2326676 secs]  
  2. [Full GC 18690K->18690K(262080K), 0.1701866 secs  



之后再执行 

 

 

[html] view plaincopy
 
  1. java -jar -verbose:gc -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  
[html] view plaincopy
 
  1. java -jar -verbose:gc -XX:+UseParNewGC -Xmn64m -Xms256m -Xmx256m SwingSet2.jar  



系统将输出如下信息(片段〕

[java] view plaincopy
 
  1. [Full GC 56048K->18869K(260224K), 0.3104852 secs]  
[java] view plaincopy
 
  1. [Full GC 56048K->18869K(260224K), 0.3104852 secs]  



提示:此选项在Heap Size 比较大而且Major收集时间较长的情况下使用更合适。

 

 

例7:关于-server选项

 

 在JVM中将运行中的类认定为server-class的时候使用此选项。SUN 的Hot Spot JVM5 如果判断到系统的配置满足如下条件则自动将运行的类认定为server-class,并且会自动设置jvm的选项(当没有手工设置这选项的时候〕而且 HOTSPOT JVM5提供了自动调优的功能,他会根据JVM的运行情况进行调整。如果没有特别的需要是不需要太多的人工干预的。SUN形象的称这个机制为“人体工学 ”(Ergonomics〕。具体可以参考http://java.sun.com/docs/hotspot/gc5.0/ergo5.html
*.具有2个或更多个物理的处理器
*.具有2G或者更多的物理内存
提示:此选项要放在所有选项的前面。例如:java -server 其他选项 java类

 

 

 

 

 

 

 

 

 

-----------------------------------------------------------------

 

 

 

 

 

  

   JVM监控

1. ParNew表示CMS收集器在新生代采用多线程进行垃圾回收。DefNew表示顺序垃圾收集器在新生代采用单线程进行垃圾回收。

2.系统做完CMS cycle后,观察minorGC的日志,如果日志中发现CMS收集前后的heap占用下降不明显,那要么是已有的对象太少,以至于找到的unreachable对象很少,这样浪费费CPU;要么是对象从新生代提拔到老生代的速率超过了CMS所能承受的范围。总之,出现以上情况表示需要进行性能调优。

3. -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled,PermGen一般存放一些JVM的元数据(Class/反射代理等),比如Spring/Hibernate大量采用cglib,导致生成的Proxy会比较多,而这些都是存放在PermGen区域,默认情况下CMS不会去做回收,因此,为了保证PermGen空间正常可以设置以上两个参数实现垃圾的回收。另外,-XX:CMSInitiatingPermOccupancyFraction=<percent>可以知道持久代的占用百分比。

4. -XX:MaxTenuringThreshold=10,意思是说对象在survivor区域中经过10minorGC还存活的话就提拔到老生代。再比如 -XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0这样的配置实际上就是去掉了survivor区域,直接将对象从eden区域提拔到老生代。

5. 在CMS中如果监控到它的两个暂停阶段initial mark or remark的暂停时间比一次minorGC的暂停时间还长,那么需要进行性能优化。

6. 当fullGC发生时伴随着持久代class的unload,那么需要考虑调大持久代空间的大小。

 

Java代码  收藏代码

[Full GC[Unloading class sun.reflect.GeneratedConstructorAccessor3]  

[Unloading class sun.reflect.GeneratedConstructorAccessor8]  

[Unloading class sun.reflect.GeneratedConstructorAccessor11]  

[Unloading class sun.reflect.GeneratedConstructorAccessor6]  

8566K->5871K(193856K), 0.0989123 secs]  

7. 使用jstack去挖掘锁竞争的方法:Observing multiple thread stack traces trying to lock the same lock address is an indication the application is experiencing lock contention,查看那些处在同一lock地址上的线程栈。     

8. JVM监控常用到的命令:-XX:+PrintGCDetails、-XX:+PrintGCTimeStamps、-XX:+PrintGCDateStamps、-Xloggc:<filename>、-XX:+PrintGCApplicationConcurrentTime and -XX:+PrintGCApplicationStoppedTime。

9.JVM监控常用的可视化工具:GCHisto、JConsole、jvisualvm

JVM调优

方法论

1.The client runtime is specialized for rapid startup, small memory footprint, and a JIT compiler with rapid code generation。The server runtime offers

more sophisticated code generation optimizations, which are more desirable in server applications。现在还出来一个新的runtime—tiered,但是还不成熟,tiered combines the best of the client and server runtimes, that is, rapid startup time and high performing generated code。

2. 选择JVM为32bit还是64bit取决于内存和第三方本地库的使用,All native components using the Java Native Interface (JNI) in a 64-bit JVM must be compiled in 64-bit mode。

 

3. -XX:+UseParallelOldGC 和 -XX:+UseParallelGC的区别:前者会启动新生代和老生代的多线程收集;而后者只启动新生代的多线程收集,老生代是单线程的。

4. 决定JVM性能的属性:1. 吞吐量;2. 延时(latency);3.内存占用量

5. JVM调优的三原则:1. Minor GC Reclamation Principle—在minorGC时要最大化回收对象数;2. GC Maximize Memory Principle;3. 2 of 3 GC Tuning

Principle—调优点放在占总性能2/3的几个属性上:吞吐量/延时(latency)/内存占用量。

6. 推荐的GC日志配置:-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>。 如果要调优为低延时, -XX:+PrintGCApplicationStoppedTime(GC时应用暂停的时间)、-XX:+PrintGCApplicationConcurrentTime(GC时应用并发执行的时间)很有用。、

推荐的log配置:

 

7.  当-Xmx 和-Xms不相同时,堆size可能会发生作自动调整,但是调整时,并不会调整新生代的大小。因此-Xmn只能用于-Xmx 和-Xms拥有相同值。事实上,把-Xmx 和-Xms设置为相同的值是一个好的习惯。

8. -XX:-ScavengeBeforeFullGC可以避免fullGC时收集新生代。

9. jmap -histo:live pid命令可以强制执行一次fullGC。

 

10. GC时,以下因素影响到延时:1. 一次minorGC的持续时间;2. minorGC的频率;3. 一次fullGC的持续时间;4. fullGC的频率;1和2决定了是否需要重新定义young generation size。3和4决定了是否需要重新定义old generation size,及是否要启用并发GC(-XX:+UseConcMarkSweepGC)。

11. 可以通过像下面这样的gclog来计算平均minorGC发生频率和平均持续时间,如果发现平均平均持续时间〉期望的应用延时时间,那么就需要进一步调优:

 

12. 当需要调优某一个代的size时,一般不要改变其他代的大小,而是通过调整堆堆和要调优的代的大小来实现。

13. 关于各代size的大小,请关注以下guidelines:

     1)The old generation space size should be not be much smaller than 1.5x the live data size。

     2) Young generation space size should be at least 10% of the Java heap size, the value specified as -Xmx and -Xms。

     3)When increasing the Java heap size, be careful not to exceed the amount of physical memory available to the JVM。

14. 提拔率的计算: rate=每秒提拔到老生代的数据量=每次minorGC提拔的数据量/收集频率(s)=minorGC前后老生代空间占用量之差/收集频率(s)。如果知道现在老生代剩余的空间(用y代表),那么就可以计算出可以算出多久后,这些剩余的空间会被提拔的对象占满,公式为:m(s)=y/rate,这个m值就应该是fullGC的理想频率。

15. 对CMS的调优重点是:avoid a stop-the-world compacting garbage collection。

16. 从其它垃圾收集器转到CMS时,一个常见的原则是扩大原有老生代20%-30%。

17. 调优CMS要注意三个元素:提拔率、fragmentation(live对象之间的hole)、并发线程回收率。解决fragmentation的一种方法就是compacting 内存,但是会造成很大的延时(这个就是叫stop-the-world ),另一个解决方法是增大内存空间(使用Maximize Memory Principle),这不能完全解决fragmentation问题,但是能够减少发生的频率,最后一个方法是减低提拔率(这个可以使用Minor GC Reclamation Principle)。

Survivor的调优:

18. survivor space计算公式:survivor space size = -Xmn<value>/(-XX:SurvivorRatio=<ratio> + 2)。

19. Tenuring Threshold的概念:每次minorGC时都会计算该值,看啥时能够将一个对象提拔到老生代,它的值其实就是对象age(经历的minorGC数),对象被分配时的age=0。如果某个时刻对象的age大于Tenuring Threshold就会被提拔到老生代。事实上,-XX:MaxTenuringThreshold=<n>命令可以指定这个值(该值的范围:ranging from 0–15 for Java 5 Update 6 and later, 0–31 for Java 5 Update 5 and earlier),default maximum value=15。

20. 如果需要Tenuring Threshold监控的值,可以通过下面命令启动-XX:+PrintTenuringDistribution。下面是日志格式,其中,Desired survivor size 8388608 bytes的值=一个survivor大小*target survivor ratio(target survivor space occupancy,默认值为50%)。要保证survivor能够容纳16690480 total的live对象需要survivor拥有16690480 /50% = 33,380,960的空间也就是32M。假设原来的配置为:-Xmx1536m -Xms1536m -Xmn512m -XX:SurvivorRatio=30,现在我们采用增加新生代空间不动eden空间的调优方式,那么配置应该变成-Xmx1568m -Xms1568m -Xmn544m -XX:SurvivorRatio=15。注意Xmn变为了544(512+32),SurvivorRatio=15=544/32-2。当然也可以通过不动新生代空间减小eden空间的办法,但还是尽量使用第一种方法。

-XX:TargetSurvivorRatio=<percent>能够指定target survivor space occupancy,默认值为50%。

Java代码  收藏代码

Desired survivor size 8388608 bytes, new threshold 1 (max 15)  

- age 1: 16690480 bytes, 16690480 total  

21. 以下现象暗示survivor的空间太小:1. new tenuring threshold<max tenuring threshold;2.  desired survivor size(见上图)< total surviving bytes数。

从上图来看,1〈15;8388608<16690480所以需要调优survivor大小。再看下图,15==15;16777216>7320248所以是一个比较合理的survivor配置。

Java代码  收藏代码

Desired survivor size 16777216 bytes, new threshold 15 (max 15)  

- age 1: 6115072 bytes, 6115072 total  

- age 2: 286672 bytes, 6401744 total  

- age 3: 115704 bytes, 6517448 total  

- age 4: 95932 bytes, 6613380 total  

- age 5: 89465 bytes, 6702845 total  

- age 6: 88322 bytes, 6791167 total  

- age 7: 88201 bytes, 6879368 total  

- age 8: 88176 bytes, 6967544 total  

- age 9: 88176 bytes, 7055720 total  

- age 10: 88176 bytes, 7143896 total  

- age 11: 88176 bytes, 7232072 total  

- age 12: 88176 bytes, 7320248 total  

CMS的调优:

22. CMS garbage collector的调优主要目标是保持老生代有足够可获得的空间和后续避免stop-the-world compacting garbage collections。

23. concurrent mode failure暗示了Stop-the-world compacting garbage collections in CMS,如下图,如果发生了这种情况需要通过命令-XX:CMSInitiatingOccupancyFraction=65(这里表示老生代空间占用65%)修改CMS garbage collection cycle启动时间。另外还有一个相关的命令-XX:+UseCMSInitiatingOccupancyOnly,它保证所有的CMS garbage collection cycle都按照设定的比例来启动,如果没有设置后者,那么仅有第一个CMS garbage collection cycle是按照设定的比例启动,之后会根据JVM自适应的方式调整启动时间。建议一起使用这两个命令。

CMSInitiatingOccupancyFraction值应该大于live data size(一个fullGC后heap的占用量),如果小于这个值,那么CMS collector会运行得过于频繁。一个经验值是至少1.5倍live data size。

Java代码  收藏代码

174.445: [GC 174.446: [ParNew: 66408K->66408K(66416K), 0.0000618  

secs]174.446: [CMS (concurrent mode failure): 161928K->162118K(175104K),  

4.0975124 secs] 228336K->162118K(241520K)  

24. 如果观察到CMS-initial-mark——〉CMS-concurrent-reset过程中heap的占用变化不大(比如说就几M或几十M),说明CMS cycle工作的太早,表示需要通过-XX:CMSInitiatingOccupancyFraction and -XX:+UseCMSInitiatingOccupancyOnly来增大老生代空间占用率。296358K-〉292925K变化不大。

Java代码  收藏代码

[ParNew 390868K-><strong><em>296358K</em></strong>(773376K), 0.1882258 secs]  

[CMS-initial-mark 298458K(773376K), 0.0847541 secs]  

[ParNew 401318K->306863K(773376K), 0.1933159 secs]  

[CMS-concurrent-mark: 0.787/0.981 secs]  

[CMS-concurrent-preclean: 0.149/0.152 secs]  

[CMS-concurrent-abortable-preclean: 0.105/0.183 secs]  

[CMS-remark 374049K(773376K), 0.0353394 secs]  

[ParNew 407285K->312829K(773376K), 0.1969370 secs]  

[ParNew 405554K->311100K(773376K), 0.1922082 secs]  

[ParNew 404913K->310361K(773376K), 0.1909849 secs]  

[ParNew 406005K->311878K(773376K), 0.2012884 secs]  

[CMS-concurrent-sweep: 2.179/2.963 secs]  

[CMS-concurrent-reset: 0.010/0.010 secs]  

[ParNew 387767K-><strong><em>292925K</em></strong>(773376K), 0.1843175 secs]  

下面这个就比较正常了,546360K-〉350518K变化比较大。

Java代码  收藏代码

[ParNew 640710K-><strong><em>546360K</em></strong>(773376K), 0.1839508 secs]  

[CMS-initial-mark 548460K(773376K), 0.0883685 secs]  

[ParNew 651320K->556690K(773376K), 0.2052309 secs]  

[CMS-concurrent-mark: 0.832/1.038 secs]  

[CMS-concurrent-preclean: 0.146/0.151 secs]  

[CMS-concurrent-abortable-preclean: 0.181/0.181 secs]  

[CMS-remark 623877K(773376K), 0.0328863 secs]  

[ParNew 655656K->561336K(773376K), 0.2088224 secs]  

[ParNew 648882K->554390K(773376K), 0.2053158 secs]  

[ParNew 489586K->395012K(773376K), 0.2050494 secs]  

[ParNew 463096K->368901K(773376K), 0.2137257 secs]  

[CMS-concurrent-sweep: 4.873/6.745 secs]  

[CMS-concurrent-reset: 0.010/0.010 secs]  

[ParNew 445124K-><strong><em>350518K</em></strong>(773376K), 0.1800791 secs]  

[ParNew 455478K->361141K(773376K), 0.1849950 secs]  

说明CMS cycle工作的太晚的例子(下图),CMS周期还没有走完就马上执行了fullGC。因此要减小XX:CMSInitiatingOccupancyFraction。

Java代码  收藏代码

[ParNew 742993K->648506K(773376K), 0.1688876 secs]  

[ParNew 753466K->659042K(773376K), 0.1695921 secs]  

[CMS-initial-mark 661142K(773376K), 0.0861029 secs]  

[Full GC 645986K->234335K(655360K), 8.9112629 secs]  

[ParNew 339295K->247490K(773376K), 0.0230993 secs]  

[ParNew 352450K->259959K(773376K), 0.1933945 secs]  

总之,要得到一个合理的CMSInitiatingOccupancyFraction值需要设置各种尝试值,然后观察日志数据,最终方可作出选择。

25. 以下日志内容表明系统进行了显示的fullGC(System.gc()),RMI应用会在某些时刻执行显示GC。

Java代码  收藏代码

[Full GC (System)  

[CMS: 418061K->428608K(16384K), 0.2539726 secs]  

418749K->4288608K(31168K),  

[CMS Perm : 32428K->32428K(65536K)],  

0.2540393 secs]  

[Times: user=0.12 sys=0.01, real=0.25 secs]  

26. -XX:ParallelGCThreads=<n>也能决定CMS中并发rermark线程数。在Java 6 Update 23中,ParallelGCThreads的默认值为:如果Runtime.availableProcessors()地值小于等于8,其值就是处理器的数目,否则,其值为处理数目*5/8。当机器上还运行有其他的应用时,需要将该值调整小于默认值。

27. -XX:+CMSScavengeBeforeRemark可以减少remark的duration值,因为老生代的对象有的会依赖于新生代的对象,当增加了这个命令时会在remark之前执行一次minorGC的操作,从而可以减少老生代到新生代的reachable对象数。

28. 如果应用有很多的Reference or finalizable objects,那么可以使用-XX:+ParallelRefProcEnabled来减少duration。

并行GC的调优:

29. 并行GC的调优主要是让其避免fullGC。

30. 并行GC支持(仅它支持)UseAdaptiveSizePolicy,它会自动地去调整新生代的空间,因此在对并行GC进行调优时需要关闭它-XX:-UseAdaptiveSizePolicy。调优前先要监控日志,使用如下配置:-XX:+PrintGCDateStamps, -XX:PrintGCDetails,-XX:+PrintAdaptiveSizePolicy。

31.当发生fullGC,首先确认old generation space是否大于等于1.5倍live data size,如果小就增大。其次查看日志看是否有overflow: true,如果有说明老生代空间不够大,或者说是survivor的空间太小。

 

Java代码  收藏代码

[GCAdaptiveSizePolicy::compute_survivor_space_size_and_thresh:  

survived: 446113911  

promoted: 10904856  

overflow: true  

[PSYoungGen: 6493788K->233888K(9437184K)]  

7959281K->2662511K(13631488K), 0.0797732 secs]  

[Times: user=0.59 sys=0.00, real=0.08 secs]  

32. 调优survivor空间,通过多次观察找到一个最大的sruvivored值(来自于上面的日志)假设这个值为500M,那么需要的survivor空间大小=500M/50%(TargetSurvivorRatio的默认值为50%)=1G。然后根据这个值来调整head的大小(在内存还可分配的情况下,尽量不要动老生代/eden空间的大小)。当没有办法下需要缩小老生代空间时必须保证老生代的空间值为1.5倍于live data size。如果实在内存紧张也可以考虑增大TargetSurvivorRatio值。

33. 一条原则:并行GC的开销不应该大于5%。

其他方面:

1. 如果应用有大的分配率,但是这些对象的存活期却很短,需要考虑更大的新生代空间,甚至超过老生代。

2. 如果应用的提拔率很低,那么可以考虑让old generation space<live data size。

3. 逃逸分析——Escape Analysis。-XX:+DoEscapeAnalysis

4. 偏向锁——Biased Locking。

 

5. 大内存页——Large Pages。

分享到:
评论

相关推荐

    Java语言规范和JVM规范官网文档

    Java语言规范和JVM规范是理解Java编程基础和高级特性的关键参考资料,它们定义了Java程序的语法、语义以及虚拟机的行为。本压缩包包含了Java6至Java9的版本,涵盖了一系列重要的语言和虚拟机改进,对于Java开发者来...

    linux & JVM内存结构分析

    在IT领域,Linux操作系统和Java虚拟机(JVM)是两个至关重要的组成部分。Linux作为一款开源的操作系统,被广泛应用于服务器、云计算以及嵌入式设备等。而JVM则是Java程序运行的基础,它负责解析和执行Java代码,并...

    jdk,jvm源码

    Java虚拟机(JVM)是Java程序运行的核心,它负责解释和执行字节码,为Java应用程序提供了一个跨平台的运行环境。JDK(Java Development Kit)包含了开发和运行Java程序所需的所有工具,包括JVM。当我们谈论"jdk,jvm...

    内容主要涉及分布式、并发、jvm调优相关-Poet.zip

    分布式、并发和JVM调优是Java开发中的关键领域,对于构建高性能、可扩展的系统至关重要。在这篇文章中,我们将深入探讨这些主题,并基于提供的压缩包"Poet.zip"进行详细解析。 首先,我们来看看分布式系统。分布式...

    多线程、JVM复习及面试100题

    在Java编程领域,多线程和Java虚拟机(JVM)是两个至关重要的主题,尤其在面试中常常被考察。本文将围绕这两个核心概念展开,深入解析相关知识点,并结合常见的面试问题进行阐述。 首先,我们来看看多线程。在单核...

    JVM调优实战(转)

    JVM调优实战 本文档旨在介绍JVM调优实战的各个方面,包括JVM内存、垃圾回收、性能优化等。通过对JVM内存结构、垃圾回收机制和性能优化策略的详细讲解,帮助读者深入理解JVM的工作原理和优化方法。 1. JVM内存结构 ...

    Java高并发编程与JVM性能调优实战 视频教程 下载下载因为太大存百度云盘3.zip

    《Java高并发编程与JVM性能调优实战》是一门深度探讨Java开发中高并发处理和JVM优化的视频教程。课程旨在帮助开发者提升在大规模并发环境下的编程能力,同时掌握JVM(Java虚拟机)的性能调优技巧,以确保应用程序的...

    jvm 启动过程 JVM 原理

    Java虚拟机(JVM)是Java程序运行的基础,它是一个抽象的计算机系统,负责执行Java字节码。本文将深入探讨JVM的启动过程及其基本原理。 首先,我们需要理解JVM的基本概念。JVM是Java Virtual Machine的缩写,它是...

    JVM中文指令手册.pdf

    JVM(Java Virtual Machine,Java虚拟机)是运行所有Java程序的假想计算机,是Java程序的运行环境,负责执行指令、管理数据、内存、寄存器等,是实现Java跨平台特性的关键部分。JVM指令手册详细记录了JVM的所有操作...

    浅谈jvm原理

    "浅谈 JVM 原理" JVM(Java Virtual Machine)是一种虚拟机,它可以模拟完整的硬件系统功能,运行在一个完全隔离的环境中,提供了一个完整的计算机系统。JVM 可以分为三类:VMWare、Visual Box 和 JVM。其中,...

    JVM必知必会

    ### JVM必知必会知识点梳理 #### 1. JVM的定义与层次 Java虚拟机(JVM)具有多重含义: - **一套规范**:即Java虚拟机规范,定义了Java虚拟机应该具有的行为。 - **一种实现**:例如HotSpot、J9、JRockit,它们都是...

    慢慢琢磨jvm 经典

    ### 深入解析JVM:Java虚拟机的精髓与挑战 #### JVM概览与重要性 JVM,即Java Virtual Machine(Java虚拟机),是Java程序员必须掌握的核心技术之一。初学者通常从简单的“HelloWorld”程序开始,逐渐接触更复杂的...

    JVM原理讲解和调优,详细讲解JVM底层

    JVM(Java虚拟机)是Java语言运行的基础,它负责执行Java字节码,并且是Java跨平台特性的关键实现。JVM的主要职责包括加载Java程序、验证字节码、将字节码转换成机器码执行、内存管理、垃圾回收和提供安全机制等。...

    JVM指令手册_jvm指令手册_

    Java虚拟机(JVM)是Java程序运行的核心,它负责解释和执行Java字节码,为应用程序提供了一个跨平台的运行环境。`JVM指令手册`是深入理解JVM内部工作原理的重要参考资料,它详细列出了JVM所支持的所有操作指令,这...

    JVM

    Java虚拟机(JVM)是Java编程语言的核心组成部分,它为Java程序提供了运行环境,使得Java代码能够在不同的操作系统上“一次编写,到处运行”。JVM是Java平台的一部分,负责执行字节码,管理内存,垃圾收集,以及提供...

    深入JVM内核—原理、诊断与优化

    《深入JVM内核—原理、诊断与优化》是一份深度探索Java虚拟机(JVM)的视频教程,旨在帮助开发者全面理解JVM的工作机制,掌握性能诊断技巧,并能进行有效的优化。本教程覆盖了从基础到高级的JVM主题,不仅适用于Java...

    jvmjava,java实现的JVM。.zip

    《Java实现的JVM——深入理解与实践》 在计算机科学领域,Java虚拟机(JVM)是Java语言的关键组成部分,它使得Java程序能够在不同平台上运行而无需重新编译。本项目“jvmjava”是一个开源项目,由Java语言实现,...

    jvm笔记

    JVM 笔记 今天,我们要讨论的是 JVM 的笔记,这个笔记包括了 JVM 的基本结构、类加载机制、类文件格式等内容。 首先,让我们来看一下 JVM 的基本结构。JVM 可以分为三个部分:CLASS LOADER、RUN TIME DATA AREA 和...

    java基础之JVM

    ### Java基础之JVM #### 一、JVM简介 **1.1 JVM是什么** Java Virtual Machine (JVM),即Java虚拟机,是Java技术的核心组成部分之一。它是一种抽象计算机,能够执行Java字节码(一种中间代码)的虚拟机环境。JVM...

Global site tag (gtag.js) - Google Analytics