Java GC 详解

1、基本回收算法

(1) 引用计数(Reference Counting)
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。 
(2) 标记-清除(Mark-Sweep) 
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。 
(3)复制(Copying) 
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。 
(4)标记-清除-压缩(Mark-Sweep-Compact) 
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。 
(5)并发(增量)收集(Incremental Collecting) 
实施垃圾回收算法,即:在应用进行的同时进行垃圾回收。

(6)并行收集(Parallel Collecting)
实施垃圾回收算法,即:在应用进行的同时进行垃圾回收。 
(7)分代(Generational Collecting) 
基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。

 

2、分代垃圾回收详述

(1)Young(年轻代)
年轻代分三个区。一个Eden区,两个Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。 
(2)Tenured(年老代) 
年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。

(3)Perm(持久代)
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:PermSize= -XX:MaxPermSize=进行设置。

 

3、GC类型
(1)Scavenge GC 
一般情况下,当新对象生成,并且在Eden申请空间失败时,就好触发Scavenge GC,堆Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。 
(2)Full GC 
对整个堆进行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,因此应该尽可能减少Full GC。有如下原因可能导致Full GC:

  • Tenured被写满
  • Perm域被写满
  • System.gc()被显示调用
  • 上一次GC之后Heap的各域分配策略动态变化

4、垃圾回收算法

(1)串行收集器

 使用单线程处理所有垃圾回收工作,因为无需多线程交互,所以效率比较高。但是,也无法使用多处理器的优势,所以此收集器适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。可以使用-XX:+UseSerialGC打开。-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。

 

(2)并行收集器
对年轻代进行并行垃圾回收,因此可以减少垃圾回收时间。一般在多线程多处理器机器上使用。使用-XX:+UseParallelGC.打开。并行收集器在J2SE5.0 update6上引入在Java SE6.0中进行了增强--可以对年老代进行并行收集。如果年老代不使用并发收集的话,使用单线程进行垃圾回收,因此会制约扩展能力。使用-XX:+UseParallelOldGC打开。 
使用-XX:ParallelGCThreads=设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相等。

 

此收集器可以进行如下配置:


最大垃圾回收暂停: 指定垃圾回收时的最长暂停时间,通过-XX:MaxGCPauseMillis=指定。为毫秒.如果指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值。设定此值可能会减少应用的吞吐量。 
吞吐量: 吞吐量为垃圾回收时间与非垃圾回收时间的比值,通过-XX:GCTimeRatio=来设定,公式为1/(1+N)。

 

例如,-XX:GCTimeRatio=19时,表示5%的时间用于垃圾回收。默认情况为99,即1%的时间用于垃圾回收。

 

 --适用情况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。
--缺点:应用响应时间可能较长 

(3)并发收集器
可以保证大部分工作都并发进行(应用不停止),垃圾回收只暂停很少的时间,此收集器适合对响应时间要求比较高的中、大规模应用。使用 -XX:+UseConcMarkSweepGC打开。

并发收集器主要减少年老代的暂停时间,他在应用不停止的情况下使用独立的垃圾回收线程,跟踪可达对象。在每个年老代垃圾回收周期中,在收集初期并发收集器会对整个应用进行简短的暂停,在收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。并发收集器使用处理器换来短暂的停顿时间。在只有一个处理器的主机上使用并发收集器,设置为incremental mode模式也可获得较短的停顿时间。


浮动垃圾: 由于在应用运行的同时进行垃圾回收,所以有些垃圾可能在垃圾回收进行完成时产生,这样就造成了“Floatin Garbage”,这些垃圾需要在下次垃圾回收周期时才能回收掉。

所以,并发收集器一般需要20%的预留空间用于这些浮动垃圾。


Concurrent Mode Failure :并发收集器在应用运行时进行收集,所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用,否则,垃圾回收还未完成,堆空间先满了。这种情况下将会发生“并发模式失败”,此时整个应用将会暂停,进行垃圾回收。 通过设置-XX:CMSInitiatingOccupancyFraction=指定还有多少剩余堆时开始执行并发收集 

 --适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。 

 

二、常用配置举例

1、heap size 设置 

JVM 中最大堆大小有三方面限制:

  • 相关操作系统的数据模型(32-bt还是64-bit)限制;
  • 系统的可用虚拟内存限制;
  • 系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。

(1) java -Xms1024m -Xmx1024m -Xmn512m -Xss128k

 

此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn:设置年轻代大小。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

 

(2) java -Xms1024m -Xmx1024m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=8 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0 -XX:SurvivorRatio=65535


-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论,应把SurvivorRatio设成最大值以最大化Eden空间。


2、吞吐量优先的并行收集器
并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。

典型配置: 
java -Xmx3800m -Xms3800m -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区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

 

3、响应时间优先的并发收集器
并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。


典型配置: 
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:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

 

4、GC log 辅助信息
(1)-XX:+PrintGC 
输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] 
[Full GC 121376K->10414K(130112K), 0.0650971 secs]

 

(2)-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]

 

(3)-XX:+PrintGCTimeStamps 
输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

 

(4)-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
输出形式:Application time: 0.5291524 seconds

 

(5)-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用
输出形式:Total time for which application threads were stopped: 0.0468229 seconds

 

(6)-XX:+PrintHeapAtGC:打印GC前后的详细堆栈信息

(7) -Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。

 

 

三、BEA JRockit JVM简介

       Bea WebLogic 8.1使用的新的JVM用于Intel平台。在Bea安装完毕的目录下可以看到有一个类似于jrockit81sp1_141_03的文件夹。这就是Bea新JVM所在目录。不同于HotSpot把Java字节码编译成本地码,它预先编译成类。JRockit还提供了更细致的功能用以观察JVM的运行状态,主要是独立的GUI控制台(只能适用于使用Jrockit才能使用jrockit81sp1_141_03自带的console监控一些cpu及memory参数)或者WebLogic Server控制台。

Bea JRockit JVM支持4种垃圾收集器: 
1. 分代复制收集器 
它与默认的分代收集器工作策略类似。对象在新域中分配,即JRockit文档中的nursery。这种收集器最适合单cpu机上小型堆操作。

2. 单空间并发收集器 
该收集器使用完整堆,并与背景线程共同工作。尽管这种收集器可以消除中断,但是收集器需花费较长的时间寻找死对象,而且处理应用程序时收集器经常运行。如果处理器不能应付应用程序产生的垃圾,它会中断应用程序并关闭收集。

       3. 分代并发收集器

       分代并发收集器这种收集器在护理域使用排它复制收集器,在旧域中则使用并发收集器。由于它比单空间共同发生收集器中断频繁,因此它需要较少的内存,应用程序的运行效率也较高,注意,过小的护理域可以导致大量的临时对象被扩展到旧域中。这会造成收集器超负荷运作,甚至采用排它性工作方式完成收集。

. 并行收集器
该收集器也停止其他进程的工作,但使用多线程以加速收集进程。尽管它比其他的收集器易于引起长时间的中断,但一般能更好的利用内存,程序效率也较高。

默认状态下,JRockit使用 分代并发收集器。要改变收集器,可使用-Xgc:,对应四个收集器分别为gencopy,singlecon,gencon以及parallel。可使用-Xms和-Xmx设置堆的初始大小和最大值。

要设置护理域,则使用-Xns:  

java –jrockit –Xms512m –Xmx512m –Xgc:gencon –Xns128m…

   注意 :如果 使用JRockit JVM的话还可以使用WLS自带的console(C:\bea\jrockit81sp1_141_03\bin下)来监控一些数据,如cpu,memery等。

要想能构监控必须在启动服务时startWeblogic.cmd中加入-Xmanagement参数。

 

 

 四、Java监控工具

 1. jmap

         查看heapdump

 

 2. jstack

        查看javacore

 

 3.jps

       列出jvm进程

 

 4.jstatd
启动jvm监控服务。它是一个基于rmi的应用,向远程机器提供本机jvm应用程序的信息。默认端口1099。 
实例:jstatd -J-Djava.security.policy=my.policy -n 10.104.46.203 -p 1099

 

my.policy文件需要自己建立,内如如下:
grant codebase "file:$JAVA_HOME/lib/tools.jar" {
permission java.security.AllPermission;
};
这是安全策略文件,因为jdk对jvm做了jaas的安全检测,所以我们必须设置一些策略,使得jstatd被允许作网络操作

 

 5.jconsole
一个图形化界面,可以观察到java进程的gc,class,内存等信息。虽然比较直观,但是个人还是比较倾向于使用jstat命令(在最后一部分会对jstat作详细的介绍)。
-Dcom.sun.management.jmxremote.port=18904 
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=false 
-Djava.awt.headless=true

 

 6.jstat

这是jdk命令中比较重要,也是相当实用的一个命令,可以观察到classloader,compiler,gc相关信息
具体参数如下:
-class:统计class loader行为信息
-compile:统计编译行为信息
-gc:统计jdk gc时heap信息
-gccapacity:统计不同的generations(不知道怎么翻译好,包括新生区,老年区,permanent区)相应的heap容量情况
-gccause:统计gc的情况,(同-gcutil)和引起gc的事件
-gcnew:统计gc时,新生代的情况
-gcnewcapacity:统计gc时,新生代heap容量
-gcold:统计gc时,老年区的情况
-gcoldcapacity:统计gc时,老年区heap容量
-gcpermcapacity:统计gc时,permanent区heap容量
-gcutil:统计gc时,heap情况
-printcompilation:不知道干什么的,一直没用过。

 

一般比较常用的几个参数是:
(1) jstat -class 2083 1000 10 (每隔1秒监控一次,一共做10次) 
输出内容含义如下:

Loaded Number of classes loaded. 
Bytes Number of Kbytes loaded. 
Unloaded Number of classes unloaded. 
Bytes Number of Kbytes unloaded. 
Time Time spent performing class load and unload operations.

 

(2) jstat -gc 2083 2000 20(每隔2秒监控一次,共做10)
输出内容含义如下:

S0C  Current survivor(存活的) space 0 capacity (KB).  
EC  Current eden space capacity (KB). 
EU  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  Number of young generation GC Events. 
YGCT  Young generation garbage collection time. 
FGC  Number of full GC events. 
FGCT  Full garbage collection time. 
GCT  Total garbage collection time.

 

五、其他事项

 

 

 1. 仔细了解自己的应用,如果用了缓存,那么年老代应该大一些,缓存的HashMap不应该无限制长,
建议采用LRU算法的Map做缓存,LRUMap的最大长度也要根据实际情况设定。

 

 2. 垃圾回收时promotion failed是个很头痛的问题,一般可能是两种原因产生,
第一个原因是救助空间不够,救助空间里的对象还不应该被移动到年老代,但年轻代又有很多对象需要放入救助空间;
第二个原因是年老代没有足够的空间接纳来自年轻代的对象;这两种情况都会转向Full GC,网站停顿时间较长。
第一个原因我的最终解决办法是去掉救助空间,设置-XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0即可,
第二个原因我的解决办法是设置CMSInitiatingOccupancyFraction为某个值(假设70),
这样年老代空间到70%时就开始执行CMS,年老代有足够的空间接纳来自年轻代的对象。

 

 3. 64位jdk似乎不能设置MaxTenuringThreshold,一旦设置MaxTenuringThreshold=0,CMS也有停滞。

 

 4.

 # cat /proc/meminfo | grep Huge
HugePages_Total: 0
HugePages_Free: 0
Hugepagesize: 2048 kB#

If the output shows the three "Huge" variables then your system can support large page memory, but it needs to be configured. 
If the command doesn't print out anything, then large page support is not available. 
To configure the system to use large page memory, one must log in as root, then: 
Increase SHMMAX value. It must be larger than the Java heap size. 
On a system with 4 GB of physical RAM (or less) the following will make all the memory sharable:
# echo 4294967295 > /proc/sys/kernel/shmmax 
Specify the number of large pages. In the following example 3 GB of a 4 GB system are reserved for large pages 
(assuming a large page size of 2048k, then 3g = 3 x 1024m = 3072m = 3072 * 1024k = 3145728k, and 3145728k / 2048k = 1536): 
# echo 1536 > /proc/sys/vm/nr_hugepages 
Note the /proc values will reset after reboot so you may want to set them in an init script (e.g. rc.local or sysctl.conf).

 

5.

-Xnoclassgc                           禁用类垃圾回收,性能会高一点
-XX:+CMSParallelRemarkEnabled 
在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间
-XX:+CMSClassUnloadingEnabled

-XX:+CMSPermGenSweepingEnabled,使CMS收集持久代的类,而不是fullGC
-XX:LargePageSizeInBytes=128M         内存页的大小, 不可设置过大, 会影响Perm的大小。
-XX:+UseFastAccessorMethods            原始类型的快速优化   get,set 方法转成本地代码 
-XX:+AggressiveHeap 特别说明下:(我感觉对于做java cache应用有帮助)  1.6 支持
试图是使用大量的物理内存,使得Xmx无效。
长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 
至少需要256MB内存 
大量的CPU,内存, (在1.4.1在4CPU的机器上已经显示有提升) 
-XX:+AggressiveOpts 加快编译
-XX:+UseBiasedLocking 锁机制的性能改善。
-XX:MinHeapFreeRatio=<n> 
指定 jvm heap 在使用率小于 n 的情况下 ,heap 进行收缩 ,Xmx==Xms 的情况下无效 , 如 :-XX:MinHeapFreeRatio=30 
-XX:MaxHeapFreeRatio=<n> 
指定 jvm heap 在使用率大于 n 的情况下 ,heap 进行扩张 ,Xmx==Xms 的情况下无效 , 如 :-XX:MaxHeapFreeRatio=70 
-XX:+UseCMSInitiatingOccupancyOnly 
指示只有在 old generation 在使用了初始化的比例后 concurrent collector 启动收集 
-XX:+UseGCOverheadLimit
当有过多的时间花费在垃圾收集上的时候,并行垃圾收集器会跑出 OutOfMemoryError 错误:
如果超过 98% 的时间花费在垃圾收集上并且只有 2% 的堆被释放的话,就会抛出一个OutOfMemory。
这个功能是用来防止堆太小导致程序长时间无法正常工作而设计的。

-XX:SoftRefLRUPolicyMSPerMB=0 
Soft reference在虚拟机中比在客户机中存活的更长一些。其清除频率可以用命令行参数 -XX:SoftRefLRUPolicyMSPerMB=<N>来控制,这可以指定每兆堆空闲空间的 soft reference 保持存活(一旦它不强可达了)的毫秒数,这意味着每兆堆中的空闲空间中的 soft reference 会(在最后一个强引用被回收之后)存活1秒钟。注意,这是一个近似的值,因为 soft reference 只会在垃圾回收时才会被清除,而垃圾回收并不总在发生。

 

 

6. (增量模式)i-cms

 

并发垃圾收集器可以在这样一种模式下工作:并发阶段以增量的方式进行。回忆一下,在并发阶段,垃圾回收线程会使用一个或多个处理器。所谓增量模式是指减少长并发阶段的影响,周期性中断并发阶段,将处理器资源还给应用程序。这种模式又称为“i-cms”,将垃圾收集器的并发工作划分到小块时间,在年轻代垃圾收集之间进行。这个功能对于那些工作在没那么多处理器的机器上(1或2个处理器的)需要并发垃圾收集器的低时延应用非常有用。

 

并发垃圾收集周期通常包括如下几步:

  • 停止所有的应用线程,标记从根开始可达的对象集,然后继续所有的应用线程
  • 在应用线程运行的同时,使用一个或更多的处理器,并发跟踪可达的对象图
  • 使用一个处理器,并发跟踪对象图中在上一步开始之后的各个改动的部分
  • 停止所有的应用线程,重新跟踪根和对象图中自从上次检查开始发生了变化的部分,然后继续运行线程
  • 使用一个处理器,并发地把不可达对象清理到用于分配空间的 free list 上面去。
  • 使用一个处理器并发地调整堆的大小,准备下一个回收周期所需的数据结构


正常情况下,并发垃圾收集器在并发跟踪阶段使用一个或多个处理器,不会让出它们。类似的,在清理阶段也会始终独占地使用一个处理器。这对于对于一个程序的响应时间可能是个不小的影响,特别是系统中只有一两个CPU的时候。增量模式通过将并发阶段分解为一系列的突发行为来降低这一影响,这些突发行为会散布在小回收之间。

 

i-cms 使用占空比来控制并发收集器自发的放弃处理器之前的工作量。占空比是年轻代收集之间的允许并发垃圾收集器运行时间的百分比。i-cms 可以根据应用的行为自动计算占空比(这也是推荐的方法,称为自动步长(auto pacing)),当然,也可以通过命令行指定一个固定的值。

(1)命令行参数

下面是控制 i-cms的命令行参数(参考下文的初始设置建议):

参数


描述


缺省值


J2SE 5.0 及以前


Java SE 6 及以后


-XX:+CMSIncrementalMode 启动增量模式。注意,并发垃圾收集器必须也被选择(-XX:+UseConcMarkSweepGC) ,否则此参数无效。 disabled disabled -XX:+CMSIncrementalPacing 打开自动步长,这样,增量模式占空比将根据JVM统计到的信息自动调整。 disabled enabled -XX:CMSIncrementalDutyCycle=<N> 两次小回收之间的允许并发收集器运行的时间的百分比(0-100)。如果打开自动步长,那么这个值就是初始值。 50 10 -XX:CMSIncrementalDutyCycleMin=<N> 自动步长打开后,占空比值的下限 (0-100)。 10 0 -XX:CMSIncrementalSafetyFactor=<N> 计算占空比值时使用的一个裕量(0-100) 10 10 -XX:CMSIncrementalOffset=<N> 在小回收之间,增量模式中占空比开始的时间,或说是向右的平移量(0-100) 0 0 -XX:CMSExpAvgFactor=<N> 当进行并发回收统计,计算指数平均值时,当前采样所用的权值(0-100) 25 25



(2) 建议参数

要在 Java SE 6 里使用 i-cms,需要使用如下命令行参数

 

-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps

前两个参数分别启动并发垃圾收集器和 i-cms。后两个参数不是必须的,它们只是要求垃圾收集器将诊断信息打印到标准输出,这样,垃圾收集器的行为就可以被看到并用于以后分析了。

注意,对于 J2SE 5.0 和之前的版本,我们建议 i-cms 使用如下的初始命令行参数:

 

-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps \
-XX:+CMSIncrementalPacing -XX:CMSIncrementalDutyCycleMin=0
-XX:CMSIncrementalDutyCycle=10

这样,就是用了和 Java SE 6 一致的参数了,多出的三个参数用于自动调整占空比。这些多余的参数值完全是使用的 Java SE 6 的缺省值。


(3) 基本问题处理

i-cms 的自动占空比计算模式使用了程序运行时收集到的统计信息进行占空比计算,以保证并发垃圾收集器可以在堆占满之前完成。不过,使用过去的行为预测将来的变化的估计方式可能并不总是足够准确,可能在某些情况下无法阻止堆用满。如果需要收集的垃圾太多,可以尝试下面这些步骤,一次使用一个:

 

Step Options 1. 增加保险系数 -XX:CMSIncrementalSafetyFactor=<N> 2. 增加最小占空比 -XX:CMSIncrementalDutyCycleMin=<N> 3. 关闭自动占空比计算,使用固定占空比 -XX:-CMSIncrementalPacing -XX:CMSIncrementalDutyCycle=<N>

 

 7. 显式垃圾回收
应用程序和垃圾回收器的另一个交互途径是显式调用 System.gc() 进行完整的垃圾回收。这回强制进行一次主回收,即使没有必要(也就是说一次小回收可能就足够了),所以应该避免这种情况。显式垃圾回收对性能的影响可以通过使用 -XX:+DisableExplicitGC 进行比较来进行测量,这样虚拟机会无视 System.gc() 的。

 

最常见的显式调用垃圾回收的场景是 RMI 的分布式垃圾回收。使用 RMI 的应用会引用到其他虚拟机中的对象。在这种分布式应用的场景下,本地堆中的垃圾可能不能被回收掉,所以 RMI 会周期性强制进行完整的垃圾回收。这些回收的频率可以使用参数来控制。如

java -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 ...

这里指定了垃圾回收每小时运行一次,而不是缺省的每分钟一次。不过,这可能会导致某些对象的清除消耗太长时间。这些参数可以被设置到高达Long.MAX_VALUE来让显式垃圾回收的间隔时间无限长,如果没有合适的DGC上限时间的话。

 

 

 

调优策略总结

  

-Xms512m -Xmx512m -Xmn100m(类似-XX:NewSize -XX:MaxNewSize) -XX:PermSize=256m -XX:MaxPermSize=256m -Xss128k -XX:ParallelGCThreads=6 -XX:SurvivorRatio=8 -XX:+UseParNewGC(1.5以后不用了) -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSPermGenSweepingEnabled 
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5 -XX:CMSInitiatingOccupancyFraction=80 -XX:+DisableExplicitGC -Xloggc:/tmp/gc.log

-XX:+CMSClassUnloadingEnabled  -XX:+AggressiveOpts -XX:+UseBiasedLocking

猜你喜欢

转载自wb8206656.iteye.com/blog/2287570
今日推荐