JVM调优

来源:互联网 发布:数据整合系统sci 编辑:程序博客网 时间:2024/06/06 03:19

  在JVM启动参数中,可以设置跟内存、垃圾回收相关的一些参数设置,默认情况不做任何设置JVM会工作的很好,但对一些配置很好的Server和具体的应用必须仔细调优才能获得最佳性能。通过设置我们希望达到一些目标:

1.GC的时间足够的小

2.GC的次数足够的少

3.发生Full GC的周期足够的长

  前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,我们只能取其平衡。

 

.内存查看工具和GC日志分析

-XX:+printGCdetails 详细了解GC中的变化。

-XX:+PrintGCTimeStamps 了解这些垃圾收集发生的时间,自JVM启动以后以秒计量。

-Xloggc:/usr/aaa/dump/heap_trace.txt 将日志信息输出到log

-XX:+HeapDumpOnOutOfMemoryError 控制OutOfMemoryError时打印堆的信息

-XX:HeapDumpPath Heap Dump文件的路径,其中不设-XX:HeapDumpPath时,dump出的文件在/tomcat_home/bin目录下

-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息

-verbose.gc 显示GC的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。

-XX:+PrintClassHistogram 打印出实例的数量以及空间大小

 

二.最大堆大小和最小堆大小

针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值

 

.针对新生代和旧生代的比例

更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC

更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率

建议新生代占整个堆1/3合适,相关JVM参数如下:

-Xms:初始堆大小

-Xmx:最大堆大小

- Xmn:新生代大小

-XX:PermSize=n:持久代最大值

-XX:MaxPermSize=n:持久代最大值

-XX:NewRatio=n:设置新生代和旧生代的比值。如:为3,表示新生代与旧生代比值为1:3,新生代占整个新生代旧生代和的1/4。默认的比例(1:2)分配堆内存。

如何选择应该依赖应用程序对象生命周期的分布情况:如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大(比如使用了本地缓存)。但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:

(A)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理

B)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,但应该给年老代至少预留1/3的增长空间。

 

.Eden和Survivor的比例

如果Eden太小,会导致频繁GC

如果Eden太大,会导致大对象直接进入旧生代,降低对象在新生代存活时间

-XX:SurvivorRatio=n:新生代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5

-XX:PretenureSizeThreshold:直接进入旧生代中的对象大小,设置此值后,大于这个参数的对象将直接在旧生代中进行内存分配。

-XX:MaxTenuringThreshold:对象转移到旧生代中的年龄,每个对象经历过一次新生代GC(Minor GC)后,年龄就加1,到超过设置的值后,对象转移到旧生代。

如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

 

.采用正确的垃圾收集器

VM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

 

a.并行收集器设置

串行收集器在GC时会停止其他所有工作线程(stop-the-world),CPU利用率是最高的,所以适用于要求高吞吐量(throughput)的应用,但停顿时间(pause time)会比较长,所以对web应用来说就不适合,因为这意味着用户等待时间会加长。而并行收集器可以理解是多线程串行收集,在串行收集基础上采用多线程方式进行GC,很好的弥补了串行收集的不足,可以大幅缩短停顿时间(如下图表示的停顿时长高度,并发比并行要短),因此对于空间不大的区域(如young generation),采用并行收集器停顿时间很短,回收效率高,适合高频率执行。

 

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

-XX:+UseParallelOldGC 多核、大内存的机器上,可以为年老代选择并行收集算法(默认为Serial单线程收集)

-XX:ParallelGCThreads=n 设置并行收集器收集时并行收集线程数

-XX:MaxGCPauseMillis=n 设置并行收集最大暂停时间,仅对ParallelScavenge生效

-XX:GCTimeRatio=n 设置垃圾回收时间占程序运行时间的百分比,仅对Parallel Scavenge生效

-XX:+UseAdaptiveSizePolicy 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

 

b.并发收集器设置

并发收集器GC时GC线程和应用线程大部分时间是并发执行,只是在初始标记(initial mark)和二次标记(remark)时需要stop-the-world,这可以大大缩短停顿时间(pause time),所以适用于响应时间优先的应用,减少用户等待时间。由于GC是和应用线程并发执行,只有在多CPU场景下才能发挥其价值,在执行过程中还会产生新的垃圾floating garbage,如果等空间满了再开始GC,那这些新产生的垃圾就没地方放了,这时就会启动一次串行GC,等待时间将会很长,所以要在空间还未满时就要启动GC。mark和sweep操作会引起很多碎片,所以间隔一段时间需要整理整个空间,否则遇到大对象,没有连续空间也会启动一次串行GC。采用此收集器(如tenured generation),收集频率不能大,否则会影响到cpu的利用率,进而影响吞吐量。


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

-XX:CMSInitiatingOccupancyFraction:默认设置下,CMS收集器在旧生代使用了68%的空间后就会被激活。此参数就是设置旧生代空间被使用多少后触发垃圾收集。注意要是CMS运行期间预留的内存无法满足程序需要,就会出现concurrent mode failure,这时候就会启用Serial Old收集器作为备用进行旧生代的垃圾收集。

这个参数设置很有技巧,基本上满足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn就不会出现promotion failed。

比如在我的应用中Xmx是6000,Xmn是500,那么Xmx-Xmn是5500M,也就是年老代有5500MCMSInitiatingOccupancyFraction=90说明年老代到90%满的时候开始执行对年老代的并发垃圾回收(CMS),这时还剩10%的空间是5500*10%=550M,所以即使Xmn(也就是年轻代共500兆)里所有对象都搬到年老代里,550兆的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的promotion failed(晋升失败)


-XX:+UseCMSCompactAtFullCollection:空间碎片过多是标记-清除算法的弊端,此参数设置在FULL GC后再进行一个碎片整理过程

-XX:CMSFullGCsBeforeCompaction:设置在若干次垃圾收集之后再启动一次内存碎片整理

较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行以上配置。

 

.线程堆栈的设置

线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。

-XX:ThreadStackSize 

-Xss

它们都用于用于设置每个线程的栈内存,默认1M。这两个参数在1.6以前,都是谁设置在后面,谁就生效;1.6版本以后,-Xss设置在后面,则以-Xss为准,-XXThreadStackSize设置在后面,则主线程以-Xss为准,其它线程以-XX:ThreadStackSize为准。

1 0