文章目录

JMeter是Apache旗下一款性能测试工具。本文用JMeter来测试不同GC参数下Tomcat的吞吐率,使用的Tomcat版本是7.0.55.0(目前最新版是8.0.12,这是7.0版的最后一个版本),JMeter的版本是2.11(目前最新版),JDK的版本是jdk1.6.0_45。

简单的Tomcat测试页搭建非常简单,先去下载Tomcat的二进制包apache-tomcat-7.0.55.tar.gz。解压这个包之后,运行bin目录下的startup.sh就可以启动Tomcat,在地址http://localhost:8080就能看到一个测试页面,如下

如果需要对JVM添加参数的话,需要设置$JAVA_OPTS环境变量。为了看的更清楚,我修改了bin目录下的catalina.sh,在start之前增加一行echo "JAVA_OPTS: "$JAVA_OPTS。这样,对不同的GC设置,启动Tomcat就比较简单,如下,

1
2
3
4
5
6
7
8
9
10
11
12
XXXX@XXXXX:/home/program/tomcat7/bin$ export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M"
XXXX@XXXXX:/home/program/tomcat7/bin$ echo $JAVA_OPTS
-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M
XXXX@XXXXX:/home/program/tomcat7/bin$ ./startup.sh
Using CATALINA_BASE: /home/program/tomcat7
Using CATALINA_HOME: /home/program/tomcat7
Using CATALINA_TMPDIR: /home/program/tomcat7/temp
Using JRE_HOME: /home/program/jdk1.6.0_45/jre
Using CLASSPATH: /home/program/tomcat7/bin/bootstrap.jar:/home/program/tomcat7/bin/tomcat-juli.jar
JAVA_OPTS: -server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M
Tomcat started.
XXXX@XXXXX:/home/program/tomcat7/bin$

JMeter的使用也比较简单,我参考了12。先新增一个线程组(Thread Group),对这个线程组增加一个HTTP请求Sampler和一个Aggregate Graph监视器。线程组设置10个线程,循环1000次,详细配置如下,

地址192.168.1.6就是Tomcat服务器的地址,最后的Aggregate Graph是汇总测试结果的,以上配置为实验的固定配置,每次测试完成之后要清空Aggregate Graph的结果,不然会和下一次的测试结果混在一起。下面,分别设置最大堆大小从32M一直到512M(固定最小堆为32M),测试Tomcat的吞吐率。以下是每项测试具体的JVM参数,

1
2
3
4
5
6
7
8
9
10
# Max Heap = 32M
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M"
# Max Heap = 64M
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx64M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M"
# Max Heap = 128M
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx128M -Xms32$$M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M"
# Max Heap = 256M
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx256M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M"
# Max Heap = 512M
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx512M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M"

以下是测试的结果,和Aggregate Graph的截图,

最大堆大小 吞吐率(/小时)
32M 1789.5
64M 2060.6
128M 2009.2
256M 1997.6
512M 1970.1

从实验结果可以看出,JVM的最大堆并不是越大越好,对于我们的这个测试案例,大约在64M的时候性能最好,之后再增加最大堆大小,性能反而会有一点点下降。

下面用-Xmx32M -Xms32M做GC参数,测试不同垃圾回收器对Tomcat吞吐率的影响,由于测试的对象是GC的不同,所以需要把堆设置的小一些,这样可以让GC发生多次,结果比较明显。测试对比的GC包括,SerialGC(Serial+SerialOld),ParNewGC(ParNew+SerialOld),CMSGC(ParNew+CMS+SerialOld),ParallelGC(Parallel+SerialOld),ParallelOldGC(Parallel+ParallelOld)。因此,实验的GC参数如下,

1
2
3
4
5
6
7
8
9
10
# SerialGC
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseSerialGC -XX:PermSize=32M"
# ParNewGC
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParNewGC -XX:PermSize=32M"
# CMSGC
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseConcMarkSweepGC -XX:PermSize=32M"
# ParallelGC
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:PermSize=32M"
# ParallelOldGC
export JAVA_OPTS="-server -Xloggc:gc.log -XX:+PrintGCDetails -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelOldGC -XX:PermSize=32M"

以下是测试的结果,和Aggregate Graph的截图,

GC的类别 吞吐率(/小时)
SerialGC 1789.5
ParNewGC 1856.3
CMSGC 2005.2
ParallelGC 1913.5
ParallelOldGC 1967.0

可以看出,在这个测试案例下,吞吐率最高的是CMSGC,最差的是SerialGC。由于我的测试机是多核的,SerialGC效果比较差应该是一个显而易见的结果,同时CMSGC的胜出也无愧于它是目前最受欢迎的GC3

对于多核的系统而言,CMSGC应该是目前HotSpot JVM上最适合的GC了,其并行度相对其他GC都要高,吞吐量也比较高。在调优CMSGC的时候,吞吐量和停顿时间是一对tradeoff,调优一个,另一个就会受到一定损失。因为,较低的停顿时间意味着每次的GC速度都很快,而GC的某些步骤是无法并行的,因此必须挂起应用进行GC,这其中引入了一次上下文切换的开销,所以必须多次的进行GC,以免一次GC延时过长,而多次GC的结果就是上下文切换的开销增多,系统用于GC的时间变多,吞吐量下降。CMS的设计是以低延时为目标的,因此,为了获得高的吞吐量,可能需要一些额外的GC参数,如-XX:GCTimeRatio等,或者更简单的,增大JVM可用内存。

本文还参考了456

文章目录

欢迎来到Valleylord的博客!

本博的文章尽量原创。