- Docker 安装 InfluxDB
- JMeter 配置 InfluxDB数据源
- Grafana 配置influxdb数据源
- DB/Redis
- 存储过程生成商品数据
- JMeter 压测示例
- Backend Listener - InfluxDB
- 参数化 CSV Data Set Config – loginTokens、productId、memberId
- HTTP Header Manager
- Simple Controller
- 聚合报告 – Aggregate Report
- 察看结果树 – View Results Tree
- JMeter 压测性能分析
- 用arthas命令分析下jvm情况
dashboard、thread、jvm
- 调整Gateway Netty 线程池大小
- 分析JVM参数
- 用jdk自带的一些命令分析下jvm情况
- 分析jstat参数分析GC回收情况
- 分析jmap查看下对象内存占用情况
- 分析jinfo当前堆内存情况
- gateway对cpu性能要求较高,机器性能问题
- 用arthas命令分析下jvm情况
- 压测创建订单接口
- order服务的grafana,发现gc还算比较正常,gc主要也是发生在年轻代
- 进入下order服务的容器,用arthas观察下是否有锁等待的情况
- 执行下thread命令看下线程整体情况,发现waiting的线程好像有点多,关键占用cpu还比较高
- 查看线程栈 – 线程都是在等待获取数据库的连接
- 查看Mysql连接过少 – 调整Order mysql服务端的总连接数
- 查看Mysql连接过少 – 调整tmysql服务端的总连接数
- waiting的线程也减少了很多,等待数据库连接的线程已经几乎没有了,但是整体感觉线程都比较忙 – 调整tomcat线程数据
- 再次压测分析慢查询SQL — bool索引
- JVM配置模板
Docker 安装 InfluxDB
docker pull influxdb:1.8.6 # 拉取influxdb镜像
docker run -d -p 8086:8086 --name=jmeterdb influxdb:1.8.6 # 启动influxdb,并命名为jmeterdb
docker exec -it jmeterdb bash # 进入容器
influx # 进入influxdb数据库
create database jmeter; # 创建jmeter库
show databases; # 显示所有数据库,显示jmeter库就创建成功
use jmeter; # 进入jmeter库
select * from jmeter; # 查询库里面的数据,这时数据是空的正常
JMeter 配置 InfluxDB
在jmeter线程组下添加后端监听器
线程组 > 监听器 > 后端监听器
Thread Group > Listener > Backend Listener
Backend Listener implementation: org.apache.jmeter.visualizers.backend.influxdb.InfluxdbBackendListenerClient
influxdbUrl: http://influxdb.host:8086/write?db=jmeter # 这里的IP输自己主机的
application: mx-app # 这里的名字自己随意定义即可
measurement: jmeter # 数据库的名字,jmeter为上面在influxdb中创建的jmeter库
testTitle: Jmeter # 这个名字也自己随意定义即可
配置完之后执行一次压测脚本,看influxdb中jmeter库里面有没有数据,有数据就配置成功了。
Grafana配置influxdb数据源
配置influxdb数据源
在grafana添加influxdb数据源,点击按钮Add data source
找到 influxdb,单击选择该db,配置influxdb数据源
拉到页面最下面点击 Save&Test 按钮
引入数据
点击左侧加号,选择Import
将json文本复制/粘贴到paste JSON 文本框中,单机Load按钮导入(json文件下载地址:https://grafana.com/api/dashboards/5496/revisions/1/download)
在DB name 中选择我们上面创建的数据源mx-mall,单机 Import 按钮完成 Dashboard 导入
DB/Redis
存储过程生成商品数据
建表
show create table pms_product;
CREATE TABLE `pms_product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`brand_id` bigint(20) DEFAULT NULL,
`product_category_id` bigint(20) DEFAULT NULL,
`feight_template_id` bigint(20) DEFAULT NULL,
`product_attribute_category_id` bigint(20) DEFAULT NULL,
`name` varchar(64) NOT NULL,
`pic` varchar(255) DEFAULT NULL,
`product_sn` varchar(64) NOT NULL COMMENT '货号',
`delete_status` int(11) DEFAULT NULL COMMENT '删除状态:0->未删除;1->已删除',
`publish_status` int(11) DEFAULT NULL COMMENT '上架状态:0->下架;1->上架',
`new_status` int(11) DEFAULT NULL COMMENT '新品状态:0->不是新品;1->新品',
`recommand_status` int(11) DEFAULT NULL COMMENT '推荐状态;0->不推荐;1->推荐',
`verify_status` int(11) DEFAULT NULL COMMENT '审核状态:0->未审核;1->审核通过',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`sale` int(11) DEFAULT NULL COMMENT '销量',
`price` decimal(10,2) DEFAULT NULL,
`promotion_price` decimal(10,2) DEFAULT NULL COMMENT '促销价格',
`gift_growth` int(11) DEFAULT '0' COMMENT '赠送的成长值',
`gift_point` int(11) DEFAULT '0' COMMENT '赠送的积分',
`use_point_limit` int(11) DEFAULT NULL COMMENT '限制使用的积分数',
`sub_title` varchar(255) DEFAULT NULL COMMENT '副标题',
`description` text COMMENT '商品描述',
`original_price` decimal(10,2) DEFAULT NULL COMMENT '市场价',
`stock` int(11) DEFAULT NULL COMMENT '库存',
`low_stock` int(11) DEFAULT NULL COMMENT '库存预警值',
`unit` varchar(16) DEFAULT NULL COMMENT '单位',
`weight` decimal(10,2) DEFAULT NULL COMMENT '商品重量,默认为克',
`preview_status` int(11) DEFAULT NULL COMMENT '是否为预告商品:0->不是;1->是',
`service_ids` varchar(64) DEFAULT NULL COMMENT '以逗号分割的产品服务:1->无忧退货;2->快速退款;3->免费包邮',
`keywords` varchar(255) DEFAULT NULL,
`note` varchar(255) DEFAULT NULL,
`album_pics` varchar(800) DEFAULT NULL COMMENT '画册图片,连产品图片限制为5张,以逗号分割',
`detail_title` varchar(255) DEFAULT NULL,
`detail_desc` text,
`detail_html` text COMMENT '产品详情网页内容',
`detail_mobile_html` text COMMENT '移动端网页详情',
`promotion_start_time` datetime DEFAULT NULL COMMENT '促销开始时间',
`promotion_end_time` datetime DEFAULT NULL COMMENT '促销结束时间',
`promotion_per_limit` int(11) DEFAULT NULL COMMENT '活动限购数量',
`promotion_type` int(11) DEFAULT NULL COMMENT '促销类型:0->没有促销使用原价;1->使用促销价;2->使用会员价;3->使用阶梯价格;4->使用满减价格;5->限时购',
`brand_name` varchar(255) DEFAULT NULL COMMENT '品牌名称',
`product_category_name` varchar(255) DEFAULT NULL COMMENT '商品分类名称',
`gmt_create` datetime(6) DEFAULT CURRENT_TIMESTAMP(6),
`gmt_modified` datetime(6) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='商品信息'
show create table pms_sku_stock;
CREATE TABLE `pms_sku_stock` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`product_id` bigint(20) DEFAULT NULL,
`sku_code` varchar(64) NOT NULL COMMENT 'sku编码',
`price` decimal(10,2) DEFAULT NULL,
`stock` int(11) DEFAULT '0' COMMENT '库存',
`lock_stock` int(11) DEFAULT '0' COMMENT '锁定库存',
`low_stock` int(11) DEFAULT NULL COMMENT '预警库存',
`sp1` varchar(64) DEFAULT NULL COMMENT '销售属性1',
`sp2` varchar(64) DEFAULT NULL,
`sp3` varchar(64) DEFAULT NULL,
`pic` varchar(255) DEFAULT NULL COMMENT '展示图片',
`sale` int(11) DEFAULT NULL COMMENT '销量',
`promotion_price` decimal(10,2) DEFAULT NULL COMMENT '单品促销价格',
`gmt_create` datetime(6) DEFAULT CURRENT_TIMESTAMP(6),
`gmt_modified` datetime(6) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_product_id` (`product_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=147 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='sku的库存'
存储过程生成商品数据
BEGIN
DECLARE sku_id INT DEFAULT 110001;
WHILE sku_id < 1000000 DO
INSERT INTO `micromall`.`pms_product` (
`brand_id`,
`product_category_id`,
`feight_template_id`,
`product_attribute_category_id`,
`name`,
`pic`,
`product_sn`,
`delete_status`,
`publish_status`,
`new_status`,
`recommand_status`,
`verify_status`,
`sort`,
`sale`,
`price`,
`promotion_price`,
`gift_growth`,
`gift_point`,
`use_point_limit`,
`sub_title`,
`description`,
`original_price`,
`stock`,
`low_stock`,
`unit`,
`weight`,
`preview_status`,
`service_ids`,
`keywords`,
`note`,
`album_pics`,
`detail_title`,
`detail_desc`,
`detail_html`,
`detail_mobile_html`,
`promotion_start_time`,
`promotion_end_time`,
`promotion_per_limit`,
`promotion_type`,
`brand_name`,
`product_category_name`
)
VALUES
(
'58',
'29',
'0',
'1',
'耐克NIKE 男子 气垫 休闲鞋 AIR MAX 90 ESSENTIAL 运动鞋 AJ1285-101白色41码',
'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b19403eN9f0b3cb8.jpg',
'6799345',
'0',
'1',
'1',
'1',
'0',
'0',
'0',
'499.00',
NULL,
'0',
'0',
'0',
'耐克NIKE 男子 气垫 休闲鞋 AIR MAX 90 ESSENTIAL 运动鞋 AJ1285-101白色41码',
'',
'499.00',
'100',
'0',
'',
'0.00',
'0',
'',
'',
'',
'',
'',
'',
'',
'',
NULL,
NULL,
'0',
'0',
'NIKE',
'男鞋'
);
INSERT INTO `micromall`.`pms_sku_stock` (
`product_id`,
`sku_code`,
`price`,
`stock`,
`lock_stock`,
`low_stock`,
`sp1`,
`sp2`,
`sp3`,
`pic`,
`sale`,
`promotion_price`
)
VALUES
(
sku_id,
sku_id,
'3000.00',
'100000000',
'10',
'5',
'黑色',
'16g',
NULL,
NULL,
'0',
NULL
);
SET sku_id = sku_id + 1;
END
WHILE;
END
JMeter 压测示例
mx-mall压测.jmx
https://download.csdn.net/download/menxu_work/87361222
Backend Listener - InfluxDB
线程组 > 监听器 > 后端监听器
Thread Group > Listener > Backend Listener
参数化 CSV Data Set Config – loginTokens、productId、memberId
线程组 > 配置元件 > CSV数据文件设置
Thread Group > Config Element > CSV Data Set Config
HTTP Header Manager
线程组 > 配置元件 > HTTP 信息头管理
Thread Group > Config Element > HTTP Header Manager
Simple Controller
线程组 > 断言 > 简单控制器
Thread Group > Assertions > Simple Controller
添加购物车接口
Body Data
{
"productId": ${
productId},
"productSkuId": ${
productId},
"quantity": 1
}
查询购物车
获取购物车项id — JSON Extractor
线程组 > 后置处理器 > JSON Extractor
Thread Group > Post Processors > JSON Extractor
创建订单接口
Body Data
{
"itemIds":[${
itemIds}]
}
获取购物车ID列表之前置处理器 – BeanShell PreProcessor
线程组 > 前置处理器 > BeanShell PreProcessor
Thread Group > Pre Processors > BeanShell PreProcessor
//log.info("调试是否获取长度id:"+vars.get("cartId_matchNr"));
int num=Integer.valueOf("${cartId_matchNr}");
//log.info("数据为:"+num);
StringBuilder stringBuilder = new StringBuilder();
for(i = 1;i<=num;i++){
stringBuilder.append(vars.get("cartId_"+i)+",");
}
String itemIds = stringBuilder.substring(0, stringBuilder.length() - 1);
//log.info("结果:"+itemIds);
vars.put("itemIds",itemIds);
聚合报告 – Aggregate Report
线程组 > 监听器 > 聚合报告
Thread Group > Listener > Aggregate Report
察看结果树 – View Results Tree
线程组 > 监听器 > 察看结果树
Thread Group > Listener > View Results Tree
性能分析
- QPS:机器情况、数据量情况、业务复杂度
- 评估经验值
追求目标:
- Mysql CPU 压下来
- Java CPU 提上去
压测添加购物车接口
启动100个线程数,循环执行次数为10000,那么每个线程发送10000次请求,总请求数为 100*10000 =1000000
Sql 问题 – 索引-- Qps 20内 到 700内
定位问题:
- grafana查看jmeter压测数据: 压测 QPS 上不去
- grafana查看Mysql数据:数据库 QPS 不大,发现有不少慢查询
- skywalking观察下整个调用链路是慢在哪一步: post请求竟然执行30多秒,其中一个mysql操作执行了近2秒
解决问题:
- Mysql explain 分析SQL语句: 全表扫描,查询商品库存表没走索引
- 添加索引重新压测,给 pms_sku_stock 表的 product_id 字段加上索引
效果:
Sql 问题 --联合索引-- Qps 700内 到 2000
QPS超过了两百多,最高的时候有四五百多,好像优化有效果,继续一直压测下去,会发现qps一直在慢慢往下掉,这是为什么了?
定位问题:
- mysql机器的cpu,发现已经严重超载了,肯定是有sql执行比较慢了
- skywalking 观察下整个调用链路是慢在哪一步:
- 之前那条 mysql Sql 的执行时间已经到了几毫秒了
- 多了一条mysql操作有一百多毫秒,拉出sql查询计划
解决问题:
- Mysql explain 分析SQL语句: 全表扫描,查询购物车表没走索引
- 添加索引重新压测, 给oms_cart_item表加上联合索引(product_id,member_id)
效果:
5. QPS 稳定在两千多,mysql的cpu占用也下降了很多,优化效果相当明显,提升了一个数量级,可见平时工作中对数据库的sql优化是非常重要的,一条慢sql在高并发场景下甚至可以拖垮整个数据库
6. skywalking这条sql的执行时间也到了几毫秒了,对应的post请求的总执行时间也下降了很多,当然还有100多毫秒,这个应该算正常,在高并发情况下,请求之间争用系统资源会有等待,导致链路调用过程中会有损耗
压测查询购物车接口
启动100个线程数,循环执行次数为10000,那么每个线程发送10000次请求,总请求数为 100*10000 =1000000
Sql 问题 --联合索引左前缀-- Qps 40 到 400
一个查询操作QPS只有40左右?
定位问题:
- skywalking 链路跟踪mysql操作耗时较多
解决问题:
- Mysql explain 分析SQL语句: 全表扫描,查询购物车表没走索引
- 添加索引重新压测, 给oms_cart_item表加上联合索引(product_id,member_id),这条sql语句没有用上,因为不满足左前缀原则,我们把索引联合索引字段排序改成(member_id,product_id)
效果:
分页–业务问题
购物车查询比添加购物车时间慢?
定位问题:
- 一个用户添加了上百件商品,造成查询慢
解决问题:
- 添加分页
Gateway JVM 问题 – 用arthas和jdk自带的一些命令分析下jvm情况
上了网关比不上网关QPS相差2倍?
用arthas命令分析下jvm情况 dashboard、thread
在容器里下载arthas, 通过arthas的一些常用命令来分析下jvm情况
wget https://arthas.gitee.io/arthas-boot.jar
java -jar arthas-boot.jar
定位问题:
-
dashboard
线程和堆占用的资源情况
-
thread
命令查看下更多的线程执行情况
thread -n 3
最繁忙的3个线程(占用cpu最多的前3个),输出栈信息
4. thread -b
输出阻塞的线程栈信息,如果响应慢,阻塞状态的线程比较多,我们需要重点关注
gateway内部的线程池很繁忙,占用cpu很高
调整Gateway Netty 线程池大小
解决问题:
- gateway可以调节netty线程池的大小,有一个参数叫
reactor.netty.ioWorkerCount
,但是一般不建议调整,因为这个线程池的内部就是按照cpu核数(我这个gateway部署的机器是8核的)来确定的,调大意义也不是很大,当然我们可以试着来调试下,在gateway的deployment文件里加上这个-Dreactor.netty.ioWorkerCount=16
Netty 默认是CPU的2倍
Gateway 默认是CPU数量
Netty Worker线程池配置:
效果: 作用不大
, 需要调整回去
分析JVM参数
- 用jvm命令看下有没有死锁
- 如果有死锁,可以通过
thread -b
命令找出死锁线程堆栈,类似下图
用jdk自带的一些命令分析下jvm情况
分析jstat参数分析GC回收情况
jstat -gcutil 7 1000 100
FGC还算正常,但是YGC几乎每秒1次,感觉有点频繁,可能是年轻代太小了,我们看下堆内存情况,当然如果FGC比较频繁我们可以通过jmap查看下对象占用内存的情况
分析jmap查看下对象内存占用情况
jmap -histo 7 | head -20
分析jinfo当前堆内存情况
jinfo -flags 1
- 当前年轻代65M
- 当前老年代130M
- 堆最大3G
定位:
- 没有设置JVM参数,所以默认初始内存是比较小的,而且默认的jvm堆设置会导致程序运行的过程中JVM堆的经常性扩容,也会影响程序性能
解决:
- 尝试着设置下把堆内存设置到1G看下效果
-Xms1G -Xmx1G -Xmn512M -Xss512K -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M
效果:
-
压测不走Gateway查询购物车接口
-
压测走Gateway查询购物车接口
- 走网关的查询购物车接口qps也接近了500左右,有一点提升,从经验上来看微服务架构里加了网关这个服务性能会有一定的损耗,但是这个损耗依然有点大,优化效果不是很明显
gateway对cpu性能要求较高,机器性能问题
因为网关gateway对cpu性能要求较高,把gateway部署调整下机器,再压测差别已经不大了。有的时候压测结果跟机器性能会有较大关系
压测创建订单接口
创建订单接口的qps不到100,凭经验来说应该是有优化余地的,注意这里grafana里展示的是3个接口的总QPS
查看skywalking的创建订单接口的链路,发现有一部分请求链路非常长,里面包含上百条查询商品的执行sql
例如:购物车添加500件商品,没查询一件商品更新一条购物车状态
解决:
- 批量更新
- 分批
进入order服务后台用arthas来分析下,然后看了下应用和mysql的机器使用率,我们发现order和gateway服务占用cpu较高,因为现在主要就在测order服务
order服务的grafana,发现gc还算比较正常,gc主要也是发生在年轻代
进入下order服务的容器,用arthas观察下是否有锁等待的情况
执行下thread命令看下线程整体情况,发现waiting的线程好像有点多,关键占用cpu还比较高
查看线程栈 – 线程都是在等待获取数据库的连接
thread thread-id
thread -n 6
我们发现里面大多数线程都是在等待获取数据库的连接
查看Mysql连接过少 – 调整Order mysql服务端的总连接数
调整Order数据库连接池大小
spring:
datasource:
url: jdbc:mysql://mysql.localhost.com:3306/micromall?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: root
password: root
druid:
initial-size: 10 #连接池初始化大小
min-idle: 20 #最小空闲连接数
max-active: 100 #最大连接数
web-stat-filter:
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" #不统计这些请求数据
stat-view-servlet: #访问监控网页的登录用户名和密码
login-username: druid
login-password: druid
查看Mysql连接过少 – 调整tmysql服务端的总连接数
数据库连接倒是用上来了,不过mysql连接已经用满了
调整tmysql服务端的总连接数
set global max_connections=500; # 这样修改mysql重启后会恢复之前值,要彻底修改需要修改mysql的配置文件并重启
waiting的线程也减少了很多,等待数据库连接的线程已经几乎没有了,但是整体感觉线程都比较忙 – 调整tomcat线程数据
server:
port: 8844
tomcat:
accept-count: 200 # accept队列的长度,当accept队列中连接的个数达到acceptCount时,新进来的请求一律被拒绝。默认值是100
threads:
max: 300 # 线程池中最大活跃线程数,默认值200
min-spare: 20 # 线程池中保持的最小线程数
max-connections: 1000 # Tomcat最多能并发处理的请求(连接)
重启order服务后再次压测发现下单qps几乎没什么变化,注意,线程数并不是越大越好的,太大了会导致cpu争用,让线程响应时间变长,所以线程数具体设置多少合适,需要通过压测具体的项目不断调整到较合理的值,比如,如果我们期望系统单台机器下单达到300并发,最大线程数设置为100或300都能达到,那我们就可以设置为100会更合适点。
既然加大tomcat线程数不行,我们再回到skywalking看下下单接口的执行链路
再次压测分析慢查询SQL — bool索引
我们发现在我们调整完各种连接池之后,虽然qps没有增加,
- 应用的cpu使用率明显降下来了,之前都是百分之两三百,
- mysql的cpu使用率依然很高,说明,mysql这边压力依然很大,但是创建订单接口链路里的sql执行都挺快
查看mysql的慢查询有没有我们没有注意到的sql
-- 慢查询
show variables like '%slow_query_log%'; -- 查看是否开启慢查询
set global slow_query_log=1; -- 开启慢查询
show variables like 'long_query_time%'; -- 查看慢查询阈值
set global long_query_time=0.1; -- 设置慢查询阈值(单位s),设置完需要重新开启session才能查看生效
show global variables like 'long_query_time'; -- 查看慢查询阈值
show variables like '%log_output%'; -- 查看慢查询写入文件还是数据表
set global log_output='TABLE'; -- 设置慢查询写入数据表,默认是写入文件
select sleep(1); -- 执行一条慢查询
select * from mysql.slow_log; -- 查看慢查询记录表
TRUNCATE mysql.slow_log;
通过慢查询我们发现竟然还有不少sql执行时间超过100ms的
分析问题:
- 执行时间较长的sql,发现基本都是下面这条sql,那我们就来优化下
- 发现这条sql走了索引,但是扫描数据有一千多条,更奇怪的是当我们执行这条sql的时候发现查询的结果竟然为空
- 这样的慢sql还挺多,并且查询结果都是空,那我们就需要结合业务代码分析下这个sql
目前系统里实现的生成订单业务:
- 在生成完订单后并没有删除购物车记录,而且修改了记录的删除状态delete_status的值为1
- sql查询的用户购物车里的还没生成的购物项数据
- 购物项数据表里可能有很多delete_status值为1的数据,虽然上面走了一个索引, 但是key_len的值我们可以发现只走了member_id的索引
- delete_status的字段,只有两个值,一般没必要建索引,但是这里是可以尝试建下索引的,因为表里的delete_status字段的值大多数都是1,而我们查询的sql更多是用0来过滤的,所以理论上建完索引,会大大减少mysql的查询扫描次数
- 给oms_cart_item表里再建一个联合索引(member_id,delete_status)
- 两个联合索引是可以合并成一个的(member_id, delete_status, product_id)
效果:
看到优化效果非常明显
- 下单接口qps超过了300
- mysql的cpu使用率明显降低了很多
- mysql的qps快上万了
- mysql的慢查询日志表里也没有再产生新的慢查询了
目前购物车与订单相关表里的数据都已经过百万了,对于这种复杂的接口在这个数据量级和目前这个配置的机器上差不多也就这个水平了
JVM配置模板
如果内存不大,比如
- 4核8G的机器,可以用默认的Parallel垃圾收集器
- 对停顿时间有一定要求,Jdk 1.8版本可以使用ParNew+CMS垃圾收集器组合,比如下面配置
- 大内存的服务,对单机并发要求非常高,那么一般可以用G1垃圾收集器了
-Xms3072M -Xmx3072M -Xmn1536M -Xss1M
-XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M
-XX:SurvivorRatio=6
-XX:MaxTenuringThreshold=5
-XX:PretenureSizeThreshold=1M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFaction=92
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0