1、意图:找到Jmeter尽可能优的JVM参数
由于Jmeter在性能测试中常常出现JVM崩溃的现象,JVM Crash的瞬间留下的信息非常的少,一般只知道大致是什么原因造成了崩溃,比如Eden区占用100%等。 什么样的情况下的参数情况下JVM是不会Crash的呢? 什么样的情况下Jmeter的使用效率是最高的呢?自己其实很早就想做这样的性能对比。
2、环境概况
不同的并发下,对Jmeter的JVM参数的要求应该是不一样的。本次实验分为三种大的情况:低并发(5、10、20)、中等规模并发(40、80)、高并发(150、200)。由于本次的实验是在8 Core、16G Mem、1000m Net下进行,服务器的软硬件参数如下:
uname -a显示如下:
- [leonardo@PLATQA136019 bin]$ uname -a
- Linux PLATQA136019 2.6.9-67.ELsmp #1 SMP Wed Nov 7 13:56:44 EST 2007 x86_64 x86_64 x86_64 GNU/Linux
top具体显示如下:
- top - 13:17:02 up 173 days, 19:22, 12 users, load average: 0.06, 0.07, 0.08
- Tasks: 137 total, 1 running, 135 sleeping, 1 stopped, 0 zombie
- Cpu0 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Cpu1 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Cpu2 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Cpu3 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Cpu4 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Cpu5 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Cpu6 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Cpu7 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si
- Mem: 16406996k total, 2149688k used, 14257308k free, 372252k buffers
- Swap: 2096440k total, 224k used, 2096216k free, 1078188k cached
Java -version显示如下:
- #java -version
- java version "1.6.0_18"
- Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
- Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode)
3、场景设计
(1)在Jmeter缺省参数情况下的性能情况
在低、中、高并发情况下,监控结果:
(2) 在Jmeter 512M情况下的性能情况
- JVM_ARGS="-Xmx512m -Xms512m -Xmn64m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -X
- X:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedOops"
在低、中、高并发情况下,监控结果:
(3) 在Jmeter 1G情况下的性能情况
- JVM_ARGS="-Xmx1g -Xms1g -Xmn128m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:L
- argePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedOops"
在低、中、高并发情况下,监控结果:
(4) 在Jmeter 2G情况下的性能情况
- JVM_ARGS="-Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:L
- argePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedOops"
在低、中、高并发情况下,监控结果:
(5) 在Jmeter 3G情况下的性能情况
- JVM_ARGS="-Xmx3g -Xms3g -Xmn384m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:L
- argePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedOops"
在低、中、高并发情况下,监控结果:
(6) 在Jmeter 4G情况下的性能情况
- JVM_ARGS="-Xmx4g -Xms4g -Xmn512m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:L
- argePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedOops"
在低、中、高并发情况下,监控结果:
压力测试结果:
- Default 512M 1G 2G 3G 4G
- 并发数 TPS 响应时间 TPS 响应时间 TPS 响应时间 TPS 响应时间 TPS 响应时间 TPS 响应时间
- 5 4811.8 1 4883.3 1 4913.6 0 4937.6 0 4930.2 0 4939.2 0
- 10 6936.5 1 6980.1 1 7035.2 0 7042.3 1 7049.9 1 7051.4 1
- 20 7609.3 2 8721.2 2 8771 2 8796.9 2 8815.5 2 8816.1 2
- 40 3689.5 10 9120.2 4 9181.6 4 9202.8 4 9207.4 4 9190.2 4
- 80 3610.4 22 9332.4 8 9432.1 8 9500.9 8 9515.7 8 9516.5 8
- 100 3622.6 27 3638.3 27 9457 10 9494.5 10 9436.4 10 9490.5 10
- 120 JVM Crash 9271.2 12 9372.3 12 9421.5 12 9437.7 12 9424.4 12
- 150 3637.9 44 3635.3 41 3644.7 41 3638 41 3635.4 41 3628.1 41
- 200 3633.6 55 3629.7 55 3620 55 3644.9 54 3634.2 55 3636.8 54
4、结论
5、知识补充
(1)JVM优化之压缩普通对象指针(CompressedOops)
通常64位JVM消耗的内存会比32位的大1.5倍,这是因为对象指针在64位架构下,长度会翻倍(更宽的寻址)。
对于那些将要从32位平台移植到64位的应用来说,平白无辜多了1/2的内存占用,这是开发者不愿意看到的。
幸运的是,从JDK 1.6 update14开始,64 bit JVM正式支持了 -XX:+UseCompressedOops 这个可以压缩指针,起到节约内存占用的新参数。
什么是 OOP ?
OOP = “ordinary object pointer” 普通对象指针。
启用CompressOops后,会压缩的对象:
• 每个Class的属性指针(静态成员变量)
• 每个对象的属性指针
• 普通对象数组的每个元素指针
当然,压缩也不是万能的,针对一些特殊类型的指针,JVM是不会优化的。
比如指向PermGen的Class对象指针,本地变量,堆栈元素,入参,返回值,NULL指针不会被压缩。
CompressedOops的原理
原理,解释器在解释字节码时,植入压缩指令(不影响正常和JVM优化后的指令顺序)。
具体逻辑是,当对象被读取时,解压,存入heap时,压缩。
压缩指令伪码
! int R8; oop[] R9; // R9 is 64 bits
! oop R10 = R9[R8]; // R10 is 32 bits
! load compressed ptr from wide base ptr:
movl R10, [R9 + R8<<3 + 16]
! klassOop R11 = R10._klass; // R11 is 32 bits
! void* const R12 = GetHeapBase();
! load compressed klass ptr from compressed base ptr:
movl R11, [R12 + R10<<3 + 8]
零基压缩优化(Zero Based Compressd Oops)
零基压缩是针对压解压动作的进一步优化。
它通过改变正常指针的随机地址分配特性,强制从零开始做分配(需要OS支持),进一步提高了压解压效率。
要启用零基压缩,你分配给JVM的内存大小必须控制在4G以上,32G以下。
如果小于4G,那么JVM会使用低虚拟地址空间(low virutal address space,64位下模拟32位),这样就不需要做压解压动作了。
而对于大于32G,将采用默认的随机地址分配特性,进行压解压。
适用场景
CompressedOops,可以让跑在64位平台下的JVM,不需要因为更宽的寻址,而付出Heap容量损失的代价。
不过,它的实现方式是在机器码中植入压缩与解压指令,可能会给JVM增加额外的开销。
(2)JVM堆栈大小
32位的机器,一般设置为2G;64位机器则可以达到4G。 在正常情况下,为了防止内存的伸缩,将参数-Xmx和-Xms调节为一样的(伸缩适用于同一台机器上面多个应用的情况,为了提高各应用的内存使用率;如果只有一个Java应用则不让JVM内存大小伸缩, 减少维护伸缩的成本);-Xmn占用Xmx的1/9; -XX:PermSize=128m -Xss256k;
(3)关于 JVM 命令行标志您不知道的 5 件事
http://www.ibm.com/developerworks/cn/java/j-5things11/index.html
(4)系列文章:
类装入问题解密,第 1 部分: 类装入和调试工具介绍
http://www.ibm.com/developerworks/cn/java/j-dclp1/
http://www.ibm.com/developerworks/cn/java/j-dclp2.html
http://www.ibm.com/developerworks/cn/java/j-djc02113/
http://www.ibm.com/developerworks/cn/java/j-dclp4/
(5)大对象导致的bug
解决方案:通过jdk 增加这个 -XX:-ReduceInitialCardMarks 项,避免这个问题。
参考:http://tianya23.blog.51cto.com/1081650/436112