性能调优

1. 概述

1.1 性能调优的重要性

性能调优是系统开发中的关键环节,能够提升系统响应速度、降低资源消耗、提高用户体验,是架构师和高级开发者的必备技能。

本文内容

  • 性能调优原则:调优的基本原则和方法
  • JVM调优:堆内存、GC、JIT等调优
  • 代码优化:算法优化、数据结构优化
  • 数据库优化:SQL优化、索引优化
  • 缓存优化:缓存策略和优化
  • 并发优化:多线程性能优化
  • 监控诊断:性能监控和问题诊断

1.2 本文内容结构

本文将从以下几个方面深入探讨性能调优:

  1. 性能调优原则:调优的基本原则
  2. JVM调优:JVM参数调优
  3. 代码优化:代码层面优化
  4. 数据库优化:数据库性能优化
  5. 缓存优化:缓存策略优化
  6. 并发优化:并发性能优化
  7. 监控诊断:性能监控和诊断

2. 性能调优原则

2.1 调优原则

2.1.1 基本原则

性能调优原则

  1. 先测量,后优化:先找出性能瓶颈,再优化
  2. 80/20原则:优化20%的关键代码,解决80%的性能问题
  3. 避免过早优化:不要过早优化,先保证功能正确
  4. 权衡取舍:性能与可维护性、可读性的平衡

性能调优流程

1
2
3
4
5
6
7
8
9
10
11
12
13
1. 性能测试

2. 性能分析

3. 找出瓶颈

4. 制定优化方案

5. 实施优化

6. 验证效果

7. 持续监控

2.2 性能指标

2.2.1 关键指标

性能指标

  1. 响应时间:请求处理时间
  2. 吞吐量:单位时间处理请求数
  3. 并发数:同时处理的请求数
  4. 资源利用率:CPU、内存、IO使用率
  5. 错误率:请求失败率

3. JVM调优

3.1 堆内存调优

3.1.1 堆内存参数

堆内存调优参数

1
2
3
4
5
6
7
8
9
10
# 堆内存设置
-Xms2g # 初始堆大小(建议与-Xmx相同)
-Xmx4g # 最大堆大小
-Xmn1g # 新生代大小

# 老年代比例
-XX:NewRatio=2 # 老年代:新生代 = 2:1

# Survivor比例
-XX:SurvivorRatio=8 # Eden:Survivor = 8:1

调优建议

  1. -Xms和-Xmx设置相同:避免动态扩容,减少GC
  2. 新生代大小:通常为堆的1/3到1/4
  3. 根据应用特点调整:短生命周期对象多,增大新生代

3.2 GC调优

3.2.1 GC参数调优

G1 GC调优

1
2
3
4
5
6
# G1 GC参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200 # 最大GC暂停时间(毫秒)
-XX:G1HeapRegionSize=16m # Region大小
-XX:InitiatingHeapOccupancyPercent=45 # 堆使用率45%时开始并发标记
-XX:ConcGCThreads=4 # 并发GC线程数

Parallel GC调优

1
2
3
4
5
# Parallel GC参数
-XX:+UseParallelGC
-XX:ParallelGCThreads=4 # GC线程数
-XX:MaxGCPauseMillis=200 # 最大GC暂停时间
-XX:GCTimeRatio=19 # GC时间占比(1/(1+19)=5%)

CMS GC调优

1
2
3
4
5
# CMS GC参数
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70 # 老年代使用率70%时触发
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+CMSParallelRemarkEnabled # 并行标记

3.3 JIT调优

3.3.1 JIT编译参数

JIT编译调优

1
2
3
4
5
6
# JIT编译参数
-XX:+TieredCompilation # 启用分层编译
-XX:CompileThreshold=10000 # 编译阈值(方法调用次数)
-XX:+PrintCompilation # 打印编译信息
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining # 打印内联信息

3.4 方法区调优

3.4.1 元空间调优

元空间调优

1
2
3
4
5
6
7
# Java 8+ 元空间参数
-XX:MetaspaceSize=256m # 元空间初始大小
-XX:MaxMetaspaceSize=512m # 元空间最大大小

# Java 8之前 永久代参数
-XX:PermSize=256m
-XX:MaxPermSize=512m

4. 代码优化

4.1 算法优化

4.1.1 算法选择

算法优化示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class AlgorithmOptimization {

// 不好:O(n²)时间复杂度
public int findMaxBad(int[] array) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
if (array[j] > max) {
max = array[j];
}
}
}
return max;
}

// 好:O(n)时间复杂度
public int findMaxGood(int[] array) {
int max = Integer.MIN_VALUE;
for (int value : array) {
if (value > max) {
max = value;
}
}
return max;
}

// 使用合适的算法
public void sortExample(int[] array) {
// 小数组:使用插入排序
if (array.length < 10) {
insertionSort(array);
} else {
// 大数组:使用快速排序
quickSort(array);
}
}
}

4.2 数据结构优化

4.2.1 数据结构选择

数据结构优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.util.*;

public class DataStructureOptimization {

// 1. 选择合适的集合
public void collectionOptimization() {
// 需要快速查找:使用HashMap
Map<String, String> map = new HashMap<>();

// 需要有序:使用TreeMap
Map<String, String> sortedMap = new TreeMap<>();

// 需要线程安全:使用ConcurrentHashMap
Map<String, String> concurrentMap = new ConcurrentHashMap<>();

// 读多写少:使用CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
}

// 2. 指定初始容量,避免扩容
public void capacityOptimization() {
// 不好:默认容量,可能多次扩容
List<String> list1 = new ArrayList<>();

// 好:指定初始容量
List<String> list2 = new ArrayList<>(1000);
Map<String, String> map = new HashMap<>(1000);
}

// 3. 使用基本类型,避免装箱拆箱
public void primitiveOptimization() {
// 不好:使用包装类型,有装箱拆箱开销
Integer sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i; // 自动装箱
}

// 好:使用基本类型
int sum2 = 0;
for (int i = 0; i < 1000000; i++) {
sum2 += i;
}
}
}

4.3 字符串优化

4.3.1 字符串处理优化

字符串优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class StringOptimization {

// 1. 使用StringBuilder代替String拼接
public void stringConcatenation() {
// 不好:创建多个String对象
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // 每次创建新String对象
}

// 好:使用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result2 = sb.toString();
}

// 2. 使用StringBuilder时指定初始容量
public void stringBuilderCapacity() {
// 好:指定初始容量,减少扩容
StringBuilder sb = new StringBuilder(1000);
}

// 3. 使用字符串常量池
public void stringPool() {
// 好:使用字符串常量池
String str1 = "Hello"; // 使用常量池
String str2 = "Hello"; // 复用常量池中的对象

// 不好:每次都创建新对象
String str3 = new String("Hello"); // 创建新对象
}
}

4.4 循环优化

4.4.1 循环性能优化

循环优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class LoopOptimization {

// 1. 减少方法调用
public void reduceMethodCalls(int[] array) {
// 不好:每次循环都调用length()
for (int i = 0; i < array.length; i++) {
// ...
}

// 好:缓存length
int len = array.length;
for (int i = 0; i < len; i++) {
// ...
}
}

// 2. 使用增强for循环(对于集合)
public void enhancedForLoop(List<String> list) {
// 好:增强for循环,编译器优化
for (String item : list) {
// ...
}
}

// 3. 循环展开(编译器自动优化)
public void loopUnrolling() {
// 编译器可能会展开循环
for (int i = 0; i < 4; i++) {
// ...
}
}
}

5. 数据库优化

5.1 SQL优化

5.1.1 SQL语句优化

SQL优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
-- 1. 避免SELECT *
-- 不好
SELECT * FROM users WHERE id = 1;

-- 好:只查询需要的字段
SELECT id, name, email FROM users WHERE id = 1;

-- 2. 使用索引
-- 创建索引
CREATE INDEX idx_user_email ON users(email);

-- 使用索引查询
SELECT * FROM users WHERE email = 'test@example.com';

-- 3. 避免在WHERE子句中使用函数
-- 不好
SELECT * FROM users WHERE YEAR(create_time) = 2024;

-- 好:使用范围查询
SELECT * FROM users WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';

-- 4. 使用LIMIT限制结果集
SELECT * FROM users LIMIT 10;

-- 5. 使用JOIN代替子查询
-- 不好
SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE status = 1);

-- 好:使用JOIN
SELECT o.* FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.status = 1;

5.2 索引优化

5.2.1 索引策略

索引优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 1. 创建合适的索引
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_order_user_date ON orders(user_id, create_date);

-- 2. 复合索引顺序
-- 索引:idx_user_email_status(email, status)
-- 可以使用索引
SELECT * FROM users WHERE email = 'test@example.com';
SELECT * FROM users WHERE email = 'test@example.com' AND status = 1;

-- 不能使用索引(不符合最左前缀)
SELECT * FROM users WHERE status = 1;

-- 3. 避免过多索引
-- 索引会降低写性能,需要权衡

-- 4. 使用覆盖索引
-- 索引包含所有查询字段,避免回表
CREATE INDEX idx_user_cover ON users(id, name, email);
SELECT id, name, email FROM users WHERE id = 1; -- 使用覆盖索引

5.3 连接池优化

5.3.1 数据库连接池

连接池优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// HikariCP配置
@Configuration
public class DataSourceConfig {

@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
config.setUsername("root");
config.setPassword("password");

// 连接池优化参数
config.setMaximumPoolSize(20); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接
config.setConnectionTimeout(30000); // 连接超时(毫秒)
config.setIdleTimeout(600000); // 空闲连接超时
config.setMaxLifetime(1800000); // 连接最大生命周期
config.setLeakDetectionThreshold(60000); // 连接泄漏检测

return new HikariDataSource(config);
}
}

6. 缓存优化

6.1 缓存策略

6.1.1 缓存策略选择

缓存策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class CacheStrategy {

// 1. Cache-Aside(旁路缓存)
public User getUser(Long id) {
// 先查缓存
User user = cache.get("user:" + id);
if (user != null) {
return user;
}

// 缓存未命中,查数据库
user = userRepository.findById(id);
if (user != null) {
// 写入缓存
cache.put("user:" + id, user, 3600); // 1小时过期
}
return user;
}

// 2. Write-Through(写透)
public void updateUser(User user) {
// 更新数据库
userRepository.update(user);
// 更新缓存
cache.put("user:" + user.getId(), user);
}

// 3. Write-Back(写回)
public void updateUserWriteBack(User user) {
// 只更新缓存
cache.put("user:" + user.getId(), user);
// 异步更新数据库
asyncUpdateDatabase(user);
}
}

6.2 多级缓存

6.2.1 缓存层次

多级缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MultiLevelCache {

// L1缓存:本地缓存(Caffeine)
private final Cache<String, Object> l1Cache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();

// L2缓存:Redis
@Autowired
private RedisTemplate<String, Object> redisTemplate;

public Object get(String key) {
// 1. 查L1缓存
Object value = l1Cache.getIfPresent(key);
if (value != null) {
return value;
}

// 2. 查L2缓存
value = redisTemplate.opsForValue().get(key);
if (value != null) {
// 回填L1缓存
l1Cache.put(key, value);
return value;
}

// 3. 查数据库
value = loadFromDatabase(key);
if (value != null) {
// 写入L2缓存
redisTemplate.opsForValue().set(key, value, 1, TimeUnit.HOURS);
// 写入L1缓存
l1Cache.put(key, value);
}

return value;
}
}

6.3 缓存预热

6.3.1 预热策略

缓存预热

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class CacheWarmup {

@Autowired
private UserService userService;

@PostConstruct
public void warmup() {
// 系统启动时预热热点数据
List<Long> hotUserIds = getHotUserIds();
for (Long userId : hotUserIds) {
userService.getUser(userId); // 触发缓存加载
}
}

@Scheduled(cron = "0 0 6 * * ?") // 每天早上6点
public void scheduledWarmup() {
// 定时预热
warmup();
}
}

7. 并发优化

7.1 线程池优化

7.1.1 线程池参数

线程池优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ThreadPoolOptimization {

// 合理设置线程池大小
public ThreadPoolExecutor createOptimalThreadPool() {
int corePoolSize = Runtime.getRuntime().availableProcessors();
int maximumPoolSize = corePoolSize * 2;

return new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000),
new ThreadFactory() {
private int count = 0;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("Worker-" + (++count));
return thread;
}
},
new ThreadPoolExecutor.CallerRunsPolicy()
);
}

// CPU密集型:线程数 = CPU核心数 + 1
// IO密集型:线程数 = CPU核心数 * 2
}

7.2 锁优化

7.2.1 减少锁竞争

锁优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class LockOptimization {

// 1. 减小锁粒度
private final Map<String, Object> map1 = new HashMap<>();
private final Object lock1 = new Object();

// 不好:锁整个方法
public void updateBad(String key, Object value) {
synchronized (lock1) {
map1.put(key, value);
// 其他耗时操作
doSomethingElse();
}
}

// 好:只锁必要的代码
public void updateGood(String key, Object value) {
synchronized (lock1) {
map1.put(key, value);
}
doSomethingElse(); // 在锁外执行
}

// 2. 使用分段锁
private final Map<String, Object>[] segments = new Map[16];
private final Object[] locks = new Object[16];

{
for (int i = 0; i < 16; i++) {
segments[i] = new HashMap<>();
locks[i] = new Object();
}
}

public void put(String key, Object value) {
int segment = key.hashCode() % 16;
synchronized (locks[segment]) {
segments[segment].put(key, value);
}
}

// 3. 使用读写锁
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public Object read(String key) {
readWriteLock.readLock().lock();
try {
return map1.get(key);
} finally {
readWriteLock.readLock().unlock();
}
}
}

7.3 无锁编程

7.3.1 CAS操作

无锁编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.concurrent.atomic.*;

public class LockFreeProgramming {

// 使用原子类,避免锁
private final AtomicInteger count = new AtomicInteger(0);

public void increment() {
count.incrementAndGet(); // 无锁操作
}

// CAS操作
public void updateWithCAS(int expected, int newValue) {
while (!count.compareAndSet(expected, newValue)) {
expected = count.get();
}
}

// 使用LongAdder(高并发场景)
private final LongAdder longAdder = new LongAdder();

public void add() {
longAdder.increment(); // 性能优于AtomicLong
}
}

8. 监控诊断

8.1 性能监控

8.1.1 监控工具

性能监控工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 1. 使用JMX监控
public class PerformanceMonitor {

public void monitorExample() {
// 获取JVM内存使用
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
System.out.println("Heap used: " + heapUsage.getUsed());
System.out.println("Heap max: " + heapUsage.getMax());

// 获取线程信息
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
int threadCount = threadBean.getThreadCount();
System.out.println("Thread count: " + threadCount);
}
}

// 2. 使用Micrometer监控
@Component
public class MetricsExample {

private final MeterRegistry meterRegistry;
private final Counter requestCounter;
private final Timer requestTimer;

public MetricsExample(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.requestCounter = Counter.builder("requests.total")
.description("Total requests")
.register(meterRegistry);
this.requestTimer = Timer.builder("requests.duration")
.description("Request duration")
.register(meterRegistry);
}

public void handleRequest() {
requestCounter.increment();
requestTimer.record(() -> {
// 处理请求
});
}
}

8.2 性能分析

8.2.1 性能分析工具

性能分析工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. jstat:查看GC统计
jstat -gc <pid> 1000 10

# 2. jmap:生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>

# 3. jstack:生成线程转储
jstack <pid> > thread.dump

# 4. jvisualvm:可视化监控
jvisualvm

# 5. arthas:在线诊断工具
java -jar arthas-boot.jar

8.3 性能测试

8.3.1 压力测试

性能测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 使用JMH进行基准测试
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class PerformanceBenchmark {

private List<String> list;

@Setup
public void setup() {
list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add("Item " + i);
}
}

@Benchmark
public void testMethod1() {
// 测试方法1
}

@Benchmark
public void testMethod2() {
// 测试方法2
}
}

9. 实战案例

9.1 性能优化案例

9.1.1 案例:接口响应慢

问题:接口响应时间从50ms增加到500ms。

分析步骤

  1. 性能测试:使用压测工具测试接口
  2. 性能分析:使用jstack、jmap分析
  3. 找出瓶颈:发现数据库查询慢
  4. 优化方案
    • 添加数据库索引
    • 使用缓存
    • 优化SQL语句

优化效果

  • 响应时间:500ms → 80ms
  • 吞吐量:提升5倍

9.2 GC优化案例

9.2.1 案例:频繁Full GC

问题:系统频繁Full GC,响应变慢。

分析

  • GC日志显示:Full GC频率高
  • 堆内存使用率高
  • 对象存活时间长

优化方案

1
2
3
4
5
6
7
8
9
# 优化前
-Xms2g -Xmx2g
-XX:+UseParallelGC

# 优化后
-Xms4g -Xmx4g
-Xmn2g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200

优化效果

  • Full GC频率:降低80%
  • 平均响应时间:降低30%

10. 总结

10.1 核心要点

  1. 先测量,后优化:找出瓶颈再优化
  2. JVM调优:合理设置堆内存和GC参数
  3. 代码优化:算法、数据结构、字符串优化
  4. 数据库优化:SQL优化、索引优化、连接池优化
  5. 缓存优化:合理使用缓存,多级缓存
  6. 并发优化:线程池、锁优化、无锁编程

10.2 关键理解

  1. 性能是权衡:性能与可维护性的平衡
  2. 监控重要:完善的监控是调优的基础
  3. 持续优化:性能调优是持续的过程
  4. 避免过度优化:不要过早优化

10.3 最佳实践

  1. 建立性能基线:记录优化前的性能指标
  2. 逐步优化:一次优化一个方面
  3. 验证效果:每次优化后验证效果
  4. 持续监控:建立完善的监控体系

相关文章