第一章:Java性能基础理论与度量体系 🚀
"性能优化就像减肥,不测量就没有改进!" ------ 某位被性能问题折磨的程序员
🎯 本章学习目标
- 掌握性能指标的正确理解和测量方法
- 学会使用专业工具进行性能测试
- 建立科学的性能基准测试体系
- 避免性能优化中的常见陷阱
1.1 性能指标与度量深度解析
🕐 响应时间 (Response Time) - 用户的耐心有限公司
想象一下,你在餐厅点餐后等待上菜的过程:
- 平均等待时间:可能是15分钟,但这个数字会骗人!
- 95%分位数:95%的客人在20分钟内能吃到菜
- 99%分位数:99%的客人在30分钟内能吃到菜
- 99.9%分位数:那个倒霉的0.1%可能要等1小时!
为什么平均响应时间会"撒谎"?
java
// 模拟响应时间分布
public class ResponseTimeDemo {
public static void main(String[] args) {
// 大部分请求很快:100ms
int[] fastRequests = new int[990]; // 99%的请求
Arrays.fill(fastRequests, 100);
// 少数请求很慢:10秒
int[] slowRequests = new int[10]; // 1%的请求
Arrays.fill(slowRequests, 10000);
// 计算平均值
int totalTime = Arrays.stream(fastRequests).sum() +
Arrays.stream(slowRequests).sum();
double average = totalTime / 1000.0;
System.out.println("平均响应时间: " + average + "ms"); // 199ms
System.out.println("但是1%的用户要等: 10000ms"); // 用户体验极差!
// 这就是为什么我们需要分位数!
System.out.println("99%分位数才是: 10000ms");
}
}
Little's Law - 性能优化的黄金定律
这个定律简单到让人怀疑人生:L = λW
- L: 系统中的请求数量(就像餐厅里的客人数量)
- λ: 请求到达率(每秒新来多少客人)
- W: 平均响应时间(客人从点餐到吃完的时间)
java
/**
* Little's Law 实战演示
* 场景:一个Web服务器的性能分析
*/
public class LittleLawDemo {
public static void main(String[] args) {
// 场景1:正常情况
analyzeSystem("正常情况", 100, 0.1); // 100 QPS, 100ms响应时间
// 场景2:响应时间变慢
analyzeSystem("数据库慢查询", 100, 0.5); // 100 QPS, 500ms响应时间
// 场景3:流量激增
analyzeSystem("促销活动", 1000, 0.1); // 1000 QPS, 100ms响应时间
}
static void analyzeSystem(String scenario, double qps, double responseTime) {
double requestsInSystem = qps * responseTime;
System.out.println("\n=== " + scenario + " ===");
System.out.println("QPS (λ): " + qps);
System.out.println("响应时间 (W): " + responseTime + "s");
System.out.println("系统中请求数 (L): " + requestsInSystem);
if (requestsInSystem > 50) {
System.out.println("⚠️ 警告:系统负载过高,可能出现排队!");
}
}
}
生活中的例子:
- 银行排队:如果每分钟来3个客人,每个客人办业务需要2分钟,那么银行里平均有6个客人在排队
- 高速收费站:如果每秒通过10辆车,每辆车收费需要6秒,那么收费站前平均排着60辆车
📈 吞吐量 (Throughput) - 系统的"消化能力"
吞吐量就像餐厅的翻台率,衡量系统单位时间内能处理多少请求。
QPS vs TPS:别被名字搞混了
java
/**
* QPS vs TPS 的区别演示
*/
public class QpsVsTpsDemo {
// QPS示例:简单查询
@Test
public void qpsExample() {
// 每个查询都是独立的,简单快速
long startTime = System.currentTimeMillis();
int queryCount = 0;
while (System.currentTimeMillis() - startTime < 1000) { // 1秒内
// 模拟简单查询:查用户信息
String result = queryUserInfo("user123");
queryCount++;
}
System.out.println("QPS: " + queryCount + " queries/second");
}
// TPS示例:复杂事务
@Test
public void tpsExample() {
// 每个事务包含多个操作,需要保证一致性
long startTime = System.currentTimeMillis();
int transactionCount = 0;
while (System.currentTimeMillis() - startTime < 1000) { // 1秒内
// 模拟转账事务:包含多个数据库操作
boolean success = transferMoney("userA", "userB", 100);
if (success) {
transactionCount++;
}
}
System.out.println("TPS: " + transactionCount + " transactions/second");
}
private String queryUserInfo(String userId) {
// 模拟简单查询,耗时1ms
try { Thread.sleep(1); } catch (InterruptedException e) {}
return "User info for " + userId;
}
private boolean transferMoney(String from, String to, int amount) {
// 模拟复杂事务,包含:
// 1. 检查余额
// 2. 扣款
// 3. 加款
// 4. 记录日志
try { Thread.sleep(10); } catch (InterruptedException e) {} // 耗时10ms
return true;
}
}
通用可扩展性定律 (Universal Scalability Law)
这个定律告诉我们:系统不是无限可扩展的!
java
/**
* 通用可扩展性定律演示
* 公式:Throughput = N / (1 + α(N-1) + βN(N-1))
* N: 并发数
* α: 串行化开销(比如锁竞争)
* β: 一致性开销(比如缓存同步)
*/
public class ScalabilityLawDemo {
public static void main(String[] args) {
System.out.println("=== 理想情况(无开销)===");
for (int n = 1; n <= 16; n *= 2) {
double throughput = calculateThroughput(n, 0, 0);
System.out.printf("并发数: %2d, 吞吐量: %.2f\n", n, throughput);
}
System.out.println("\n=== 现实情况(有锁竞争)===");
for (int n = 1; n <= 16; n *= 2) {
double throughput = calculateThroughput(n, 0.1, 0.01);
System.out.printf("并发数: %2d, 吞吐量: %.2f\n", n, throughput);
}
}
static double calculateThroughput(int n, double alpha, double beta) {
return n / (1 + alpha * (n - 1) + beta * n * (n - 1));
}
}
输出结果分析:
makefile
=== 理想情况(无开销)===
并发数: 1, 吞吐量: 1.00
并发数: 2, 吞吐量: 2.00
并发数: 4, 吞吐量: 4.00
并发数: 8, 吞吐量: 8.00
并发数: 16, 吞吐量: 16.00
=== 现实情况(有锁竞争)===
并发数: 1, 吞吐量: 1.00
并发数: 2, 吞吐量: 1.82
并发数: 4, 吞吐量: 2.67
并发数: 8, 吞吐量: 3.08
并发数: 16, 吞吐量: 2.46 ← 注意:开始下降了!
关键洞察:并发不是越多越好,找到最佳并发数才是王道!
🔍 资源利用率精细化监控
资源利用率就像体检报告,告诉你系统的"健康状况"。
CPU利用率的"隐藏真相"
java
/**
* CPU利用率监控工具
*/
public class CpuMonitor {
public static void main(String[] args) throws Exception {
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
System.out.println("=== CPU监控开始 ===");
for (int i = 0; i < 10; i++) {
// 获取系统CPU使用率
double systemCpuLoad = osBean.getSystemCpuLoad() * 100;
// 获取JVM进程CPU使用率
double processCpuLoad = osBean.getProcessCpuLoad() * 100;
// 获取系统负载
double systemLoadAverage = osBean.getSystemLoadAverage();
System.out.printf("时间: %2ds | 系统CPU: %5.1f%% | 进程CPU: %5.1f%% | 负载: %.2f\n",
i, systemCpuLoad, processCpuLoad, systemLoadAverage);
// 模拟一些CPU密集型工作
if (i == 5) {
System.out.println("开始CPU密集型任务...");
startCpuIntensiveTask();
}
Thread.sleep(1000);
}
}
static void startCpuIntensiveTask() {
new Thread(() -> {
// 模拟CPU密集型任务
for (int i = 0; i < 1000000; i++) {
Math.sqrt(i);
}
}).start();
}
}
内存使用的"真实面貌"
java
/**
* 内存监控详解
*/
public class MemoryMonitor {
public static void main(String[] args) throws Exception {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
System.out.println("=== 内存监控开始 ===");
for (int i = 0; i < 5; i++) {
printMemoryInfo(memoryBean);
if (i == 2) {
System.out.println("创建大量对象...");
createManyObjects();
}
Thread.sleep(2000);
}
}
static void printMemoryInfo(MemoryMXBean memoryBean) {
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
System.out.println("\n--- 内存使用情况 ---");
System.out.printf("堆内存: 已用 %d MB / 最大 %d MB (%.1f%%)\n",
heapUsage.getUsed() / 1024 / 1024,
heapUsage.getMax() / 1024 / 1024,
(double) heapUsage.getUsed() / heapUsage.getMax() * 100);
System.out.printf("非堆内存: 已用 %d MB / 最大 %d MB\n",
nonHeapUsage.getUsed() / 1024 / 1024,
nonHeapUsage.getMax() / 1024 / 1024);
}
static void createManyObjects() {
List<String> objects = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
objects.add("Object " + i + " with some data to consume memory");
}
// 对象会在方法结束后变成垃圾,等待GC回收
}
}
1.2 性能测试方法论与实战
🎪 性能测试的"马戏团表演"
性能测试就像马戏团的表演,需要在不同的"节目"中展示系统的各种能力。
负载测试:温水煮青蛙
java
/**
* 渐进式负载测试
* 就像慢慢加热水,看青蛙什么时候跳出来
*/
public class GradualLoadTest {
private static final String TARGET_URL = "http://localhost:8080/api/test";
private static final AtomicInteger successCount = new AtomicInteger(0);
private static final AtomicInteger errorCount = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
System.out.println("=== 渐进式负载测试开始 ===");
// 从1个并发开始,每30秒增加10个并发
for (int concurrency = 1; concurrency <= 100; concurrency += 10) {
System.out.printf("\n--- 当前并发数: %d ---\n", concurrency);
ExecutorService executor = Executors.newFixedThreadPool(concurrency);
long startTime = System.currentTimeMillis();
// 运行30秒
while (System.currentTimeMillis() - startTime < 30000) {
for (int i = 0; i < concurrency; i++) {
executor.submit(() -> {
try {
// 模拟HTTP请求
simulateHttpRequest();
successCount.incrementAndGet();
} catch (Exception e) {
errorCount.incrementAndGet();
}
});
}
Thread.sleep(1000); // 每秒发送一轮请求
}
executor.shutdown();
// 计算这个并发级别的性能指标
int totalRequests = successCount.get() + errorCount.get();
double qps = totalRequests / 30.0;
double errorRate = (double) errorCount.get() / totalRequests * 100;
System.out.printf("QPS: %.1f, 错误率: %.2f%%\n", qps, errorRate);
// 如果错误率超过5%,说明系统开始不稳定
if (errorRate > 5) {
System.out.println("🚨 系统开始出现不稳定,建议最大并发数: " + (concurrency - 10));
break;
}
// 重置计数器
successCount.set(0);
errorCount.set(0);
}
}
static void simulateHttpRequest() throws Exception {
// 模拟HTTP请求的处理时间
Thread.sleep(50 + (int)(Math.random() * 100)); // 50-150ms随机延迟
// 模拟5%的请求失败
if (Math.random() < 0.05) {
throw new RuntimeException("Request failed");
}
}
}
压力测试:找到系统的"爆点"
java
/**
* 压力测试:快速找到系统极限
* 就像给气球充气,看什么时候爆炸
*/
public class StressTest {
public static void main(String[] args) throws Exception {
System.out.println("=== 压力测试开始 ===");
// 直接上高并发,看系统能否扛住
int[] concurrencyLevels = {50, 100, 200, 500, 1000};
for (int concurrency : concurrencyLevels) {
System.out.printf("\n🔥 压力测试 - 并发数: %d\n", concurrency);
StressTestResult result = runStressTest(concurrency, 60); // 运行60秒
System.out.printf("结果: QPS=%.1f, 平均响应时间=%.1fms, 错误率=%.2f%%\n",
result.qps, result.avgResponseTime, result.errorRate);
if (result.errorRate > 10) {
System.out.println("💥 系统在此并发级别下崩溃!");
break;
}
}
}
static StressTestResult runStressTest(int concurrency, int durationSeconds)
throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(concurrency);
AtomicInteger requestCount = new AtomicInteger(0);
AtomicInteger errorCount = new AtomicInteger(0);
AtomicLong totalResponseTime = new AtomicLong(0);
long startTime = System.currentTimeMillis();
long endTime = startTime + durationSeconds * 1000;
// 持续发送请求直到测试结束
while (System.currentTimeMillis() < endTime) {
executor.submit(() -> {
long requestStart = System.currentTimeMillis();
try {
simulateRequest();
long responseTime = System.currentTimeMillis() - requestStart;
totalResponseTime.addAndGet(responseTime);
requestCount.incrementAndGet();
} catch (Exception e) {
errorCount.incrementAndGet();
}
});
Thread.sleep(1); // 稍微控制一下请求频率
}
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
// 计算结果
int totalRequests = requestCount.get() + errorCount.get();
double qps = (double) totalRequests / durationSeconds;
double avgResponseTime = totalRequests > 0 ?
(double) totalResponseTime.get() / requestCount.get() : 0;
double errorRate = (double) errorCount.get() / totalRequests * 100;
return new StressTestResult(qps, avgResponseTime, errorRate);
}
static void simulateRequest() throws Exception {
// 模拟请求处理
Thread.sleep(10 + (int)(Math.random() * 90)); // 10-100ms
// 在高压力下,错误率会增加
if (Math.random() < 0.02) { // 2%基础错误率
throw new RuntimeException("Request failed under stress");
}
}
static class StressTestResult {
final double qps;
final double avgResponseTime;
final double errorRate;
StressTestResult(double qps, double avgResponseTime, double errorRate) {
this.qps = qps;
this.avgResponseTime = avgResponseTime;
this.errorRate = errorRate;
}
}
}
🛠️ 性能测试工具深度对比
JMeter:老牌劲旅的华丽转身
xml
<!-- JMeter测试计划示例:电商网站性能测试 -->
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="电商网站性能测试">
<elementProp name="TestPlan.arguments" elementType="Arguments" guiclass="ArgumentsPanel">
<collectionProp name="Arguments.arguments">
<elementProp name="baseUrl" elementType="Argument">
<stringProp name="Argument.name">baseUrl</stringProp>
<stringProp name="Argument.value">http://localhost:8080</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</TestPlan>
<hashTree>
<!-- 线程组:模拟用户行为 -->
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="用户购买流程">
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">60</stringProp>
<stringProp name="ThreadGroup.duration">300</stringProp>
</ThreadGroup>
<hashTree>
<!-- HTTP请求:登录 -->
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="用户登录">
<stringProp name="HTTPSampler.domain">${baseUrl}</stringProp>
<stringProp name="HTTPSampler.path">/api/login</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
</HTTPSamplerProxy>
<!-- HTTP请求:浏览商品 -->
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="浏览商品">
<stringProp name="HTTPSampler.domain">${baseUrl}</stringProp>
<stringProp name="HTTPSampler.path">/api/products</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>
<!-- HTTP请求:添加购物车 -->
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="添加购物车">
<stringProp name="HTTPSampler.domain">${baseUrl}</stringProp>
<stringProp name="HTTPSampler.path">/api/cart/add</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
</HTTPSamplerProxy>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
Gatling:现代化的性能测试利器
scala
// Gatling测试脚本:更现代化的方式
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class ECommerceSimulation extends Simulation {
// HTTP配置
val httpProtocol = http
.baseUrl("http://localhost:8080")
.acceptHeader("application/json")
.userAgentHeader("Gatling Performance Test")
// 用户行为场景
val scn = scenario("电商用户购买流程")
.exec(
http("登录")
.post("/api/login")
.body(StringBody("""{"username":"user${userId}","password":"password"}"""))
.check(status.is(200))
.check(jsonPath("$.token").saveAs("authToken"))
)
.pause(1, 3) // 随机暂停1-3秒,模拟真实用户行为
.exec(
http("浏览商品列表")
.get("/api/products")
.header("Authorization", "Bearer ${authToken}")
.check(status.is(200))
.check(jsonPath("$.products[*].id").findAll.saveAs("productIds"))
)
.pause(2, 5)
.exec(
http("查看商品详情")
.get("/api/products/${productIds.random()}")
.header("Authorization", "Bearer ${authToken}")
.check(status.is(200))
)
.pause(1, 2)
.exec(
http("添加到购物车")
.post("/api/cart/add")
.header("Authorization", "Bearer ${authToken}")
.body(StringBody("""{"productId":"${productIds.random()}","quantity":1}"""))
.check(status.is(200))
)
// 负载模式:模拟真实的用户访问模式
setUp(
scn.inject(
nothingFor(4 seconds), // 等待4秒开始
atOnceUsers(10), // 立即10个用户
rampUsers(50) during (1 minute), // 1分钟内逐渐增加到50个用户
constantUsersPerSec(20) during (5 minutes), // 5分钟内保持每秒20个新用户
rampUsersPerSec(20) to 50 during (2 minutes), // 2分钟内从每秒20个增加到50个
heavisideUsers(100) during (3 minutes) // 3分钟内按照Heaviside函数增加100个用户
)
).protocols(httpProtocol)
.assertions(
global.responseTime.max.lt(5000), // 最大响应时间小于5秒
global.responseTime.mean.lt(1000), // 平均响应时间小于1秒
global.successfulRequests.percent.gt(95) // 成功率大于95%
)
}
1.3 性能基准测试 (Benchmarking)
🔬 微基准测试:显微镜下的性能
微基准测试就像用显微镜观察细胞,需要极其精确和小心。
JMH:Java界的"瑞士军刀"
java
/**
* JMH微基准测试示例:字符串拼接性能对比
* 这个例子会让你重新认识String、StringBuilder和StringBuffer
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
public class StringConcatenationBenchmark {
@Param({"10", "100", "1000"})
private int iterations;
@Benchmark
public String stringConcatenation() {
String result = "";
for (int i = 0; i < iterations; i++) {
result += "Hello" + i; // 每次都创建新的String对象!
}
return result;
}
@Benchmark
public String stringBuilderConcatenation() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append("Hello").append(i); // 在内部数组中操作
}
return sb.toString();
}
@Benchmark
public String stringBufferConcatenation() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < iterations; i++) {
sb.append("Hello").append(i); // 线程安全,但有同步开销
}
return sb.toString();
}
@Benchmark
public String stringBuilderWithCapacity() {
// 预估容量,避免扩容
StringBuilder sb = new StringBuilder(iterations * 10);
for (int i = 0; i < iterations; i++) {
sb.append("Hello").append(i);
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(StringConcatenationBenchmark.class.getSimpleName())
.build();
new Runner(opt).run();
}
}
预期结果分析:
bash
Benchmark (iterations) Mode Cnt Score Error Units
StringConcatenationBenchmark.stringConcatenation 10 avgt 5 156.789 ± 8.234 ns/op
StringConcatenationBenchmark.stringConcatenation 100 avgt 5 8234.567 ± 123.456 ns/op
StringConcatenationBenchmark.stringConcatenation 1000 avgt 5 823456.789 ± 1234.567 ns/op
StringConcatenationBenchmark.stringBuilderConcatenation 10 avgt 5 45.123 ± 2.345 ns/op
StringConcatenationBenchmark.stringBuilderConcatenation 100 avgt 5 234.567 ± 12.345 ns/op
StringConcatenationBenchmark.stringBuilderConcatenation 1000 avgt 5 1234.567 ± 23.456 ns/op
关键洞察:
- String拼接的时间复杂度是O(n²),因为每次都要复制整个字符串
- StringBuilder的时间复杂度是O(n),性能随着字符串长度线性增长
- 预设容量的StringBuilder性能最佳,避免了数组扩容的开销
基准测试的常见陷阱
java
/**
* 基准测试陷阱演示:JIT编译器的"聪明"优化
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class BenchmarkTrapDemo {
private int value = 42;
@Benchmark
public void deadCodeElimination() {
// 陷阱1:死代码消除
// JIT编译器发现这个计算结果没有被使用,直接删除了!
int result = value * 2 + 1;
// 解决方案:使用Blackhole.consume(result)
}
@Benchmark
public int constantFolding() {
// 陷阱2:常量折叠
// JIT编译器在编译时就计算出了结果:85
return 42 * 2 + 1;
// 解决方案:使用@State中的变量
}
@Benchmark
public int loopUnrolling() {
// 陷阱3:循环展开
int sum = 0;
for (int i = 0; i < 10; i++) { // 小循环会被展开
sum += i;
}
return sum;
// JIT编译器可能直接返回45(0+1+2+...+9的结果)
}
// 正确的基准测试写法
@Benchmark
public void correctBenchmark(Blackhole bh) {
int result = value * 2 + 1;
bh.consume(result); // 防止死代码消除
}
@Param({"10", "100", "1000"})
private int size; // 使用参数化测试
@Benchmark
public int correctLoopBenchmark() {
int sum = 0;
for (int i = 0; i < size; i++) { // 使用变量,防止常量折叠
sum += i;
}
return sum;
}
}
📊 宏基准测试:全景视角的性能
宏基准测试就像从飞机上俯瞰城市,看到的是整体性能表现。
java
/**
* 电商系统端到端性能基准测试
*/
public class ECommerceEndToEndBenchmark {
private static final String BASE_URL = "http://localhost:8080";
private static final int CONCURRENT_USERS = 100;
private static final int TEST_DURATION_MINUTES = 10;
public static void main(String[] args) throws Exception {
System.out.println("=== 电商系统端到端性能基准测试 ===");
// 预热系统
System.out.println("系统预热中...");
warmupSystem();
// 执行基准测试
BenchmarkResult result = runBenchmark();
// 输出结果
printBenchmarkReport(result);
// 性能基准对比
compareWithBaseline(result);
}
static void warmupSystem() throws Exception {
// 预热:发送少量请求让JIT编译器优化代码
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
try {
simulateUserJourney();
} catch (Exception e) {
// 预热阶段忽略错误
}
});
}
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
// 等待系统稳定
Thread.sleep(5000);
System.out.println("预热完成!");
}
static BenchmarkResult runBenchmark() throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(CONCURRENT_USERS);
AtomicInteger totalRequests = new AtomicInteger(0);
AtomicInteger successfulRequests = new AtomicInteger(0);
AtomicLong totalResponseTime = new AtomicLong(0);
List<Long> responseTimes = Collections.synchronizedList(new ArrayList<>());
long startTime = System.currentTimeMillis();
long endTime = startTime + TEST_DURATION_MINUTES * 60 * 1000;
System.out.println("基准测试开始...");
// 持续发送请求
while (System.currentTimeMillis() < endTime) {
executor.submit(() -> {
long requestStart = System.currentTimeMillis();
try {
simulateUserJourney();
long responseTime = System.currentTimeMillis() - requestStart;
totalRequests.incrementAndGet();
successfulRequests.incrementAndGet();
totalResponseTime.addAndGet(responseTime);
responseTimes.add(responseTime);
} catch (Exception e) {
totalRequests.incrementAndGet();
// 记录失败,但不增加成功计数
}
});
Thread.sleep(10); // 控制请求频率
}
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
// 计算统计结果
long actualDuration = System.currentTimeMillis() - startTime;
double qps = (double) totalRequests.get() / (actualDuration / 1000.0);
double successRate = (double) successfulRequests.get() / totalRequests.get() * 100;
double avgResponseTime = successfulRequests.get() > 0 ?
(double) totalResponseTime.get() / successfulRequests.get() : 0;
// 计算分位数
Collections.sort(responseTimes);
long p95 = getPercentile(responseTimes, 95);
long p99 = getPercentile(responseTimes, 99);
return new BenchmarkResult(qps, successRate, avgResponseTime, p95, p99);
}
static void simulateUserJourney() throws Exception {
// 模拟完整的用户购买流程
// 1. 用户登录
Thread.sleep(50 + (int)(Math.random() * 100));
// 2. 浏览商品列表
Thread.sleep(100 + (int)(Math.random() * 200));
// 3. 查看商品详情
Thread.sleep(80 + (int)(Math.random() * 120));
// 4. 添加到购物车
Thread.sleep(60 + (int)(Math.random() * 90));
// 5. 结算支付(只有30%的用户会完成购买)
if (Math.random() < 0.3) {
Thread.sleep(200 + (int)(Math.random() * 300));
}
// 模拟5%的请求失败率
if (Math.random() < 0.05) {
throw new RuntimeException("Request failed");
}
}
static long getPercentile(List<Long> sortedList, int percentile) {
if (sortedList.isEmpty()) return 0;
int index = (int) Math.ceil(sortedList.size() * percentile / 100.0) - 1;
return sortedList.get(Math.max(0, Math.min(index, sortedList.size() - 1)));
}
static void printBenchmarkReport(BenchmarkResult result) {
System.out.println("\n=== 基准测试报告 ===");
System.out.printf("QPS: %.1f requests/second\n", result.qps);
System.out.printf("成功率: %.2f%%\n", result.successRate);
System.out.printf("平均响应时间: %.1f ms\n", result.avgResponseTime);
System.out.printf("95%%分位数: %d ms\n", result.p95);
System.out.printf("99%%分位数: %d ms\n", result.p99);
// 性能等级评估
String grade = evaluatePerformance(result);
System.out.println("性能等级: " + grade);
}
static String evaluatePerformance(BenchmarkResult result) {
if (result.successRate < 95) return "❌ 不及格 - 成功率过低";
if (result.p99 > 5000) return "❌ 不及格 - 响应时间过长";
if (result.qps < 100) return "⚠️ 及格 - QPS偏低";
if (result.qps > 1000 && result.p95 < 500) return "🏆 优秀";
if (result.qps > 500 && result.p95 < 1000) return "👍 良好";
return "✅ 中等";
}
static void compareWithBaseline(BenchmarkResult current) {
// 与历史基准对比
BenchmarkResult baseline = new BenchmarkResult(800, 98.5, 120, 200, 500);
System.out.println("\n=== 与基准对比 ===");
System.out.printf("QPS: %.1f (基准: %.1f) %s\n",
current.qps, baseline.qps,
current.qps >= baseline.qps ? "✅" : "❌");
System.out.printf("成功率: %.2f%% (基准: %.2f%%) %s\n",
current.successRate, baseline.successRate,
current.successRate >= baseline.successRate ? "✅" : "❌");
System.out.printf("P95响应时间: %d ms (基准: %d ms) %s\n",
current.p95, baseline.p95,
current.p95 <= baseline.p95 ? "✅" : "❌");
}
static class BenchmarkResult {
final double qps;
final double successRate;
final double avgResponseTime;
final long p95;
final long p99;
BenchmarkResult(double qps, double successRate, double avgResponseTime, long p95, long p99) {
this.qps = qps;
this.successRate = successRate;
this.avgResponseTime = avgResponseTime;
this.p95 = p95;
this.p99 = p99;
}
}
}
🎯 本章总结
关键要点回顾
-
性能指标不会撒谎,但会误导
- 平均值会掩盖问题,分位数才是真相
- Little's Law是性能分析的基础工具
- 资源利用率要看细节,不能只看总体
-
性能测试是一门艺术
- 负载测试找容量,压力测试找极限
- 工具只是手段,理解业务场景才是关键
- 测试环境要尽可能接近生产环境
-
基准测试需要严谨
- 微基准测试要防范JIT优化陷阱
- 宏基准测试要模拟真实用户行为
- 基准对比要有历史数据支撑
实战建议
-
建立性能监控体系
java// 在关键业务方法中添加性能监控 @Timed(name = "user.login", description = "用户登录耗时") public LoginResult login(String username, String password) { // 业务逻辑 }
-
定期进行性能基准测试
bash# 建议每周运行一次基准测试 mvn clean test -Dtest=PerformanceBenchmark
-
建立性能预警机制
yaml# Prometheus告警规则示例 - alert: HighResponseTime expr: http_request_duration_seconds{quantile="0.95"} > 1 for: 5m labels: severity: warning annotations: summary: "响应时间过长"
下章预告
下一章我们将深入JVM的内部世界,探索:
- JVM内存模型的秘密
- 垃圾回收器的工作原理
- JVM参数调优的艺术
- 生产环境JVM问题诊断
记住:性能优化的第一步是准确测量,第二步是理解原理,第三步才是动手优化!
"在性能优化的道路上,测量是我们的眼睛,理论是我们的大脑,实践是我们的双手。"