搜档网
当前位置:搜档网 › JVM调优实战

JVM调优实战

JVM调优实战
JVM调优实战

JVM调优实战

付增斌

日期:2009-05-21

目录

1 理论篇 (1)

1.1 多功能养鱼塘-JVM内存 (1)

1.2 池塘中的鱼-程序中的对象 (2)

1.3 养殖区域划分-JVM中的代 (2)

1.4 主人定期捕鱼-JVM垃圾回收 (4)

1.5 不同的捕鱼方式-垃圾回收器 (5)

1.6 捕鱼工具选择-JVM参数 (7)

2 实战篇 (16)

2.1 测试目的 (16)

2.2 测试环境准备 (16)

2.3 录制测试脚本 (17)

2.4 定义测试场景 (17)

2.5 执行初步性能测试 (17)

2.6 选择调优方案 (18)

2.7 调优后JVM监控图 (21)

2.8 测试结果分析 (24)

3 性能问题举例 (25)

3.1 性能症状 (25)

3.2 监控结果 (25)

3.3 原因分析 (28)

3.4 该系统的JVM设置 (29)

4 后记 (29)

5 附:舍得网的典型配置 (30)

1理论篇

1.1多功能养鱼塘-JVM内存

大鱼塘O(可分配内存):JVM可以调度使用的总的内存数,这个数量受操作系统进程寻址范围、系统虚拟内存总数、系统物理内存总数、其他系统运行所占用的内存资源等因素的制约。

小池塘A(堆内存):JVM运行时数据区域,它为类实例和数组分配的内存。堆可以是固定大小的也可以是可变大小的。其中Heap = {Old + NEW = { Eden , from, to } }。

小池塘B(非堆内存):包括所有线程之间共享的一个方法区域和JVM为优化或内部处理所分配的内存。它存储每一个类的结构,如一个运行时的常量池、字段和方法数据、方法的代码和构造函数。这个方法区是逻辑上堆的一部分,但依赖于实现,一个JVM可以不去回收或者压缩它。像堆一样,方法区可以固定大小的,也可以是大小可变的。方法区不是必须是连续的,它们可以是不连续的。除方法区之外,JVM总是从非堆中分配用于优化和内部处理所需的内存。例如,JIT编译器为高性能的JVM代码转换存储成本地代码而分配的内存。

整个池塘结构图如下:

查看大池塘O大小的方法为:

在命令行下用java -XmxXXXXM -version 命令来进行测试,然后逐渐的增大XXXX的值,如果执行正常就表示指定的内存大小可用,否则会打印错误信息,示例如下:java -Xmx3072M -version。

当一个URL被访问时,内存申请过程如下:

A. JVM会试图为相关Java对象在Eden中初始化一块内存区域

B. 当Eden空间足够时,内存申请结束。否则到下一步

C. JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收), 释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区

D. Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor 区的对象会被移到Old区,否则会被保留在Survivor区

E. 当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集(0级)

F. 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”

1.2池塘中的鱼-程序中的对象

程序中运行的各种类实例称之为对象,每个对象都有不同的生命周期,有的存活时间长点,有的存活时间短点,这就想鱼塘中养的不同生长期的鱼一样,有的三个月就可以上市,有的鱼则需要6个月甚至更长的时间才能上市。JVM内存机制的设置就是为了要满足这种不同生命周期的对象对内存的需求,并使之能达到最大的性能表现。

1.3养殖区域划分-JVM中的代

鱼塘主人为了充分利用现有的条件来赚取更多的利润,他需要喂养各种不同种类的鱼,于是又把鱼塘分割成了几块不同区域:“鱼苗养殖区”、“短中期养殖区”、“长期养殖区”,来养殖不同生长周期的鱼。JVM同样为了对各种不同生命周期的对象进行有效管理也划分了各种不同的区域,这就是“代”的概念,分别叫做:“青年代”、“老年代”、“持久代”,下面逐一介绍每个代的含义和作用。

短中期鱼苗养殖区-年青代(Young Generation)

年青代由一个Eden Space和两个Survivor Spaces组成,虚拟机初始时分配所有的对象到Eden Space,许多对象也是在这里死去。当它执行一个“minor GC”的时候,虚拟机将从Eden Space中移动一些残余的对象到其中的一个Survivor Spaces中。青年代就好像养鱼塘中的“中短期养殖区”一样,主人把鱼先投放到“短期养殖区”喂养,隔一段时间就开始下网捞出已经长成的那些鱼拿到集市去卖,这个过程就是从“Eden Space”中执行垃圾回收的过程。主

人接着把捕捞之后剩下的“漏网之鱼”赶到“中期养殖区”继续喂养。这个“中期养殖区”就是“Survivor Spaces”,当然鱼在“中期养殖区”喂养一段时间后也要捞出那些长成的鱼去卖,这就是对“Survivor Spaces”执行垃圾回收的过程。

Ps Eden Space: 这个内存池在对象初始化时被分配;

Ps Survivor Space: 这个内存池中包含着Eden Space 经过GC之后幸存下来的对象;

年轻代设置策略:对于响应时间优先的应用需尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达老年代的对象。对于吞吐量优先的应用则尽可能的设置大,可达到Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。

长期养殖区-老年代(老年代):、

虚拟机将在Survivor Spaces中生存足够长时间的对象移动到老年代的Tenured Spaces 中。当Tenured Generation被填满,则将执行一个完全GC,这个完全GC非常的慢,因为它要处理所有存活着的对象,用的是串行标记收集的方式,并发收集可以减少对于应用的影响。

老年代设置策略:对于响应时间优先的应用,老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可能会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。

最优化的方案,一般需要参考以下数据获得:

并发垃圾收集信息

持久代并发收集次数

传统GC信息

花在年轻代和老年代回收上的时间比例

减少年轻代和老年代花费的时间,一般会提高应用的效率

对于吞吐量优先的应用,一般吞吐量优先的应用都有一个很大的年轻代和一个较小的老年代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活对象。

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

片”,可能需要进行如下配置:-XX:+UseCMSCompactAtFullCollection 使用并发收集器时,开启对老年代的压缩;-XX:CMSFullGCsBeforeCompaction=0上面配置开启的情况下,这里设置多少次Full GC后,对老年代进行压缩。

/*监控实例*/

内存池名称: Tenured Gen

Java 虚拟机最初向操作系统请求的内存量: 3,538,944 字节

Java 虚拟机实际能从操作系统获得的内存量: 1,431,699,456 字节

Java 虚拟机可从操作系统获得的最大内存量: 1,431,699,456 字节。请注意,并不一定能获得该内存量。

Java 虚拟机此时使用的内存量: 1,408,650,472 字节

/*监控实例*/

实例说明:系统能获得的最大Tenured Generation空间大小为1.431G左右,此时使用已经1.408G,基本满了,所以在JVM执行串行标记垃圾收集时,系统响应速度会很慢!

鱼苗养殖区-持久代(Permanent Generation)

控制着所有虚拟机自己映射的数据,如类和对象的方法。在持久代中jvm则存储class 和method对象。持久代就像鱼苗养殖区一样,池塘主人一次对该区域投入足够量的鱼苗,已保证其他鱼塘的足够供应。就配置而言,永久域是一个独立域并且不认为是堆的一部分。永久域默认大小为4m.运行程序时,jvm会调整永久域的大小以满足需要。每次调整时,jvm 会对堆进行一次完全的垃圾收集。使用-XX:MaxPerSize标志来增加永久域搭大小。在WebLogic Server应用程序加载较多类时,经常需要增加永久域的最大值。当jvm加载类时,永久域中的对象急剧增加,从而使jvm不断调整永久域大小。为了避免调整,可使用-XX:PerSize标志设置初始值。

1.4主人定期捕鱼-JVM垃圾回收

一个池塘收容积限制,能养殖的鱼的数量是一定的,因此隔一段时间必须捞出部分长成的鱼来使主人能喂养很多的鱼。同样,JVM所管理的有限内存也要实现最优化利用,Garbage Collection(GC)就是用来释放没有被引用的对象所占领的内存,目的在于清除不再使用的对象。GC通过算法和参数的配置可以对性能产生效果显著的影响。

GC就好像把长成的鱼从池塘中捞出来拿到市场上去卖,然后给池塘腾出空间继续养别

的鱼赚钱。采用哪种养殖方式能让鱼塘主人赚到更大的利润是鱼塘主人的经营目的,而JVM 调优的目的在于如何能是系统表现出更好的响应时间、更大的吞吐量。

Minor Collections(局部垃圾回收):当通用内存消耗完被分配的内存时,JVM会在内存池上执行一个局部的GC(总是调用minor collection)去释放被dead的对象所占用的内存。这个局部的GC通常比完全GC要快许多。青年代中的垃圾回收就是采用局部垃圾回收机制,因此,青年代中内存分配和管理效率也是最高。

通常情况下,对于内存的申请优先在青年代中申请,当内存不够时会整理新生代,当整理以后还是不能满足申请的内存,就会向老年代移动一些生命周期较长的对象。这种整理和移动会消耗资源,同时降低系统运行响应能力,因此如果青年代设置的过小,就会频繁的整理和移动,对性能造成影响。那是否把年青代设置的越大越好,其实不然,青年代采用的是复制搜集算法,这种算法必须停止所有应用程序线程,服务器线程切换时间就会成为应用响应的瓶颈。

Major Collections(完全垃圾回收):当老年代需要被回收,这就是一个major collection ,它的运行常常非常慢,因为它要涉及所有存活着的类。

/*实例*/

垃圾收集器的名称: Copy

使用此垃圾收集器收集的数量: 219 字节

垃圾收集时间: 18 秒630 毫秒

垃圾收集器的名称: MarkSweepCompact

使用此垃圾收集器收集的数量: 47 字节

垃圾收集时间: 36 秒166 毫秒

实例说明:copy垃圾搜集器的运行时间为18秒回收219字节,回收速度为平均每秒12字节,而MKC垃圾搜集器的时间为36秒回收了47字节,回收速度为平均每秒1.3字节,两者差距几乎达到了10倍,可见完全垃圾回收的速度远不如局部垃圾回收。

1.5不同的捕鱼方式-垃圾回收器

Sun JVM提供有4垃圾回收器:

Serial Collector(序列垃圾回收器):垃圾回收器对Young Gen和Tenured Gen都是使用单线的垃圾回收方式,对Young Gen,会使用拷贝策略避免内存碎片,对Old Gen,

会使用压缩策略避免内存碎片。在JVM启动参数中使用-XX:+UseSerialGC启用Serial Collector。串行收集器只适用于小数据量的情况,默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。基本上在多内核的服务器上应该避免使用这种方式。JDK5.0以后,JVM会根据当前系统配置进行判断。串行GC适合小型应用和单处理器系统(无需多线程交互,效率比较高)。

Parallel Collector(并发垃圾回收器):垃圾回收器对Young Gen和Tenured Gen 都是使用多线程并行垃圾回收的方式,对Young Gen,会使用拷贝策略避免内存碎片,对Old Gen,会使用压缩策略避免内存碎片。在JVM启动参数中使用-XX:+UseParallelGC启用Parallel Collector。这是一种吞吐量优先的并行收集器,主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。采用了多线程并行管理和回收垃圾对象,提高了回收效率和服务器的吞吐量,适合于多处理器的服务器。

Parallel Compacting Collector(并行压缩垃圾回收器):与Parallel Collector垃圾回收类似,但对Tenured Gen会使用一种更有效的垃圾回收策略,此垃圾回收器在暂停时间上会更短。在JVM启动参数中使用-XX:+UseParallelOldGC启用Parallel Compacting Collector。这是一种响应时间优先的并发收集器,主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

Concurrent Mark-Sweep (CMS) Collector(并发标志清除垃圾回收器):对Young Gen 会使用与Parallel Collector同样的垃圾回收策略,对Tenured Gen垃圾回收的垃圾标志线程与应用线程同时进行,而垃圾清除则需要暂停应用线程,但暂停时间会大大缩减,需要注意的是,由于垃圾回收过程更加复杂,会降低总体的吞吐量。

这里说一下并行和并发的区别,并行指的是多个进程并行执行垃圾回收,那么可以很好的利用多处理器,而并发指的是应用程序不需要暂停可以和垃圾回收线程并发工作。

说明:对于联机处理的应用系统或复杂的3层应用系统,采用Concurrent Mark-Sweep (CMS) Collector进行垃圾搜集,基本上既能保证性能,又能保证稳定性(暂停时间短)。

1.6捕鱼工具选择-JVM参数

1.6.1通用JVM参数

-server

如果不配置该参数,JVM会根据应用服务器硬件配置自动选择不同模式,server模式启动比较慢,但是运行期速度得到了优化,适合于服务器端运行的JVM。

-client

启动比较快,但是运行期响应没有server模式的优化,适合于个人PC的服务开发和测试。

-Xmx

设置java heap的最大值,默认是机器物理内存的1/4。这个值决定了最多可用的Java 堆内存:分配过少就会在应用中需要大量内存作缓存或者临时对象时出现OOM(Out Of Memory)的问题;如果分配过大,那么就会因PermSize过小而引起的另外一种Out Of Memory。所以如何配置还是根据运行过程中的分析和计算来确定,如果不能确定还是采用默认的配置。

-Xms

设置Java堆初始化时的大小,默认情况是机器物理内存的1/64。这个主要是根据应用启动时消耗的资源决定,分配少了申请起来会降低运行速度,分配多了也浪费。

-XX:PermSize

初始化永久内存区域大小。永久内存区域全称是Permanent Generation space,是指内存的永久保存区域,程序运行期不对PermGen space进行清理,所以如果你的APP会LOAD

很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下用了大量的第三方jar,其大小超过了jvm默认的PermSize大小(4M)那么就会产生此错误信息了。

-XX:MaxPermSize

设置永久内存区域最大大小。

-Xmn

直接设置青年代大小。整个JVM可用内存大小=青年代大小+ 老年代大小+ 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小老年代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

按照Sun的官方设置比例,则上面的例子中年轻代的大小应该为2048*3/8=768M。

-XX:NewRatio

控制默认的Young代的大小,例如,设置-XX:NewRatio=3意味着Young代和老年代的比率是1:3。换句话说,Eden和Survivor空间总和是整个堆大小的1/4。

如图中的实际设置,-XX:NewRatio=2,-Xmx=2048,则年轻代和老年代的分配比例为1:2,即年轻代的大小为682M,而老年代的大小为1365M。查看实际系统的jvm监控结果为:

内存池名称: Tenured Gen

Java 虚拟机最初向操作系统请求的内存量: 3,538,944 字节

Java 虚拟机实际能从操作系统获得的内存量: 1,431,699,456 字节

Java 虚拟机可从操作系统获得的最大内存量: 1,431,699,456 字节。请注意,并不一定能获得该内存量。

Java 虚拟机此时使用的内存量: 1,408,650,472 字节

即:1,408,650,472 字节=1365M,证明了上面的计算是正确的。

-XX:SurvivorRatio

设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6。越大的survivor空间可以允许短期对象尽量在年青代消亡;如果Survivor空间太小,Copying收集将直接将其转移到老年代

中,这将加快老年代的空间使用速度,引发频繁的完全垃圾回收。

如下图:

SurvivorRatio的值设为3,Xmn为768M,则每个Survivor空间的大小为768M/5=153.6M。

-XX:NewSize

为了实现更好的性能,您应该对包含短期存活对象的池的大小进行设置,以使该池中的对象的存活时间不会超过一个垃圾回收循环。新生成的池的大小由NewSize 和MaxNewSize 参数确定。通过这个选项可以设置Java新对象生产堆内存。在通常情况下这个选项的数值为1024的整数倍并且大于1MB。这个值的取值规则为,一般情况下这个值-XX:NewSize是最大堆内存(maximum heap size)的四分之一。增加这个选项值的大小是为了增大较大数量的短生命周期对象。增加Java新对象生产堆内存相当于增加了处理器的数目。并且可以并行地分配内存,但是请注意内存的垃圾回收却是不可以并行处理的。作用跟-XX:NewRatio相似,-XX:NewRatio是设置比例而-XX:NewSize是设置精确的数值。

-XX:MaxNewSize

通过这个选项可以设置最大Java新对象生产堆内存。通常情况下这个选项的数值为1 024的整数倍并且大于1MB,其功用与上面的设置新对象生产堆内存-XX:NewSize相同。一般要将NewSize和MaxNewSize设成一致。

-XX:MaxTenuringThreshold

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

如下图:

-XX:MaxTenuringThreshold参数被设置成5,表示对象会在Survivor区进行5次复制后如果还没有被回收才会被复制到老年代。

-XX:GCTimeRatio

设置垃圾回收时间占程序运行时间的百分比。该参数设置为n的话,则垃圾回收时间占程序运行时间百分比的公式为1/(1+n) ,如果n=19表示java可以用5%的时间来做垃圾回收,1/(1+19)=1/20=5%。

-XX:TargetsurvivorRatio

该值是一个百分比,控制允许使用的救助空间的比例,默认值是50。该参数设置较大的话可提高对survivor空间的使用率。当较大的堆栈使用较低的SurvivorRatio时,应增加该值到80至90,以更好利用救助空间。

-Xss

设置每个线程的堆栈大小,根据应用的线程所需内存大小进行调整,在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。当这个选项被设置的较大(>2MB)时将会在很大程度上降低系统的性能。因此在设置这个值时应该格外小心,调整后要注意观察系统的性能,不断调整以期达到最优。

JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。

-Xnoclassgc

这个选项用来取消系统对特定类的垃圾回收。它可以防止当这个类的所有引用丢失之后,这个类仍被引用时不会再一次被重新装载,因此这个选项将增大系统堆内存的空间。禁用类垃圾回收,性能会高一点;

-XX:+UseSerialGC:

设置串行收集器。

1.6.3并行收集器参数

-XX:+UseParallelGC:

选择垃圾收集器为并行收集器,此配置仅对年轻代有效,即上述配置下,年轻代使用并行收集,而老年代仍旧使用串行收集。采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服务器。

-XX:ParallelGCThreads

配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

-XX:+UseParallelOldGC:

采用对于老年代并发收集的策略,可以提高收集效率。JDK6.0支持对老年代并行收集。

-XX:MaxGCPauseMillis

设置每次年轻代并行收集最大暂停时间,如果无法满足此时间,JVM会自动调整年轻代大小以满足此值。

-XX:+UseAdaptiveSizePolicy:

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

-XX:+UseConcMarkSweepGC

指定在老年代使用concurrent cmark sweep gc。gc thread 和app thread 并行( 在init-mark 和remark 时pause app thread)。app pause 时间较短, 适合交互性强的系统, 如web server。它可以并发执行收集操作,降低应用停止时间,同时它也是并行处理模式,可以有效地利用多处理器的系统的多进程处理。

-XX:+UseParNewGC

指定在New Generation 使用parallel collector, 是UseParallelGC 的gc 的升级版本, 有更好的性能或者优点, 可以和CMS gc 一起使用

-XX:+UseCMSCompactAtFullCollection:

打开对老年代的压缩。可能会影响性能,但是可以消除碎片,在FULL GC的时候,压缩内存,CMS是不会移动内存的,因此,这个非常容易产生碎片,导致内存不够用,因此,内存的压缩这个时候就会被启用。增加这个参数是个好习惯。

-XX:+CMSIncrementalMode:

设置为增量模式。适用于单CPU情况

-XX:CMSFullGCsBeforeCompaction

由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

-XX:+CMSClassUnloadingEnabled

使CMS收集持久代的类,而不是fullgc

-XX:+CMSPermGenSweepingEnabled

使CMS收集持久代的类,而不是fullgc。

-XX:-CMSParallelRemarkEnabled

在使用UseParNewGC 的情况下, 尽量减少mark 的时间。

-XX:CMSInitiatingOccupancyFraction

说明老年代到百分之多少满的时候开始执行对老年代的并发垃圾回收(CMS),这个参数设置有很大技巧,基本上满足公式:

(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn

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

如果按照Xmx=2048,Xmn=768的比例计算,则CMSInitiatingOccupancyFraction的值不能超过40,否则就容易出现垃圾回收时的promotion failed。

-XX:+UseCMSInitiatingOccupancyOnly

指示只有在老年代在使用了初始化的比例后concurrent collector 启动收集

-XX:SoftRefLRUPolicyMSPerMB

相对于客户端模式的虚拟机(-client选项),当使用服务器模式的虚拟机时(-server选项),对于软引用(soft reference)的清理力度要稍微差一些。可以通过增大-XX:SoftRefLRUPolicyMSPerMB来降低收集频率。默认值是1000,也就是说每秒一兆字节。Soft reference在虚拟机中比在客户集中存活的更长一些。其清除频率可以用命令行参数

-XX:SoftRefLRUPolicyMSPerMB=来控制,这可以指定每兆堆空闲空间的soft reference 保持存活(一旦它不强可达了)的毫秒数,这意味着每兆堆中的空闲空间中的soft reference 会(在最后一个强引用被回收之后)存活1秒钟。注意,这是一个近似的值,因为soft reference 只会在垃圾回收时才会被清除,而垃圾回收并不总在发生。

-XX:LargePageSizeInBytes

内存页的大小,不可设置过大,会影响Perm的大小。

-XX:+UseFastAccessorMethods

原始类型的快速优化,get,set 方法转成本地代码。

-XX:+DisableExplicitGC

禁止java 程序中的full gc, 如System.gc() 的调用。最好加上防止程序在代码里误用了,对性能造成冲击。

-XX:+AggressiveHeap

特别说明下:(我感觉对于做java cache应用有帮助)

试图是使用大量的物理内存

长时间大内存使用的优化,能检查计算资源(内存,处理器数量)

至少需要256MB内存

大量的CPU/内存,(在1.4.1在4CPU的机器上已经显示有提升)

-XX:+AggressiveOpts

加快编译

-XX:+UseBiasedLocking

锁机制的性能改善。

2实战篇

2.1测试目的

测试被测系统使用不同的垃圾回收方案时的性能表现;

了解各种JVM参数在性能调优时的实际效果;

对遴选出的最优方案进行8小时压力测试并记录测试结果;

2.2测试环境准备

被测程序的运行的软硬件环境:

●D630 4G内存+T7250双核CPU+160G硬盘;

●操作系统:windowsXP SP3;

●IP:11.55.15.51;

被测程序名称:

●XXX银行采购管理系统V1.1版;

程序部署环境:

●Tomcat6.0.18 for windows;

●Sun JDK1.6.13 for windows;

●Oracle10g for windows(单独运行在另外一台640M笔记本上)

性能测试工具运行的软硬件环境:

●操作系统:windowsxp sp3

●浏览器版本:IE7

●IP地址:11.55.15.141

●性能测试工具:loadrunner9.10

JVM监控工具:

●使用Jconsole进行图形化监控;

确定JVM在被测系统的机器上最大可用内存:

通过在命令行下用java -XmxXXXXM -version 命令反复测试发现在11.55.15.51机器上JVM能使用的最大内存为1592M。

2.3录制测试脚本

录制前准备:修改checkcode.java文件,将随机生成的校验码改成一个固定的校验码方便脚本的自动运行,然后将编译好的checkcode.class文件替换发布包中的class文件。

选择典型业务操作进行脚本录制,每个系统的典型业务操作都会不同,需要经过分析统计,选择用户操作频率最高的部分。经过分析后确定的脚本内容为:录制系统登录操作并在登录成功后的主界面上选取一段文字作为验证点。

启动VuGen程序按脚本定义内容进行录制并调试脚本,保证脚本能正常运行。

2.4定义测试场景

●虚拟用户数:30

●持续运行时间:8小时

●虚拟用户加载和卸载方式:同时

●性能监控指标:响应时间、吞吐量、成功交易数

2.5执行初步性能测试

使用系统默认的参数执行测试,并记录响应时间、吞吐量已经成功交易数等数据,同时监控JVM的使用情况。

相关主题