本文目录导读:

我来分享几个真实的Java性能瓶颈案例,涵盖常见的性能问题及其解决方案。
案例1:字符串拼接导致的性能问题
问题场景
// 错误示范:循环中大量字符串拼接
public String processData(List<User> users) {
String result = "";
for (User user : users) {
result += "Name: " + user.getName() + ", ";
result += "Age: " + user.getAge() + "; ";
}
return result;
}
性能分析
- 每次操作都会创建新的String对象
- 循环100万次会产生100万个临时对象
- CPU和内存开销巨大
解决方案
// 正确方式:使用StringBuilder
public String processData(List<User> users) {
StringBuilder sb = new StringBuilder(users.size() * 20);
for (User user : users) {
sb.append("Name: ").append(user.getName()).append(", ");
sb.append("Age: ").append(user.getAge()).append("; ");
}
return sb.toString();
}
// 高并发场景:ThreadLocal复用StringBuilder
public class ThreadLocalStringBuilder {
private static final ThreadLocal<StringBuilder> TL =
ThreadLocal.withInitial(() -> new StringBuilder(1024));
public static String buildString(List<User> users) {
StringBuilder sb = TL.get();
sb.setLength(0); // 清空
// ... 拼接逻辑
return sb.toString();
}
}
案例2:数据库连接未关闭
问题场景
// 错误示范:连接泄漏
public List<Order> getOrders() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM orders");
// 处理结果集...
return processOrders(rs);
} catch (SQLException e) {
log.error("DB error", e);
return Collections.emptyList();
}
// 没有finally块,连接永不关闭
}
解决方案
// 正确方式:try-with-resources
public List<Order> getOrders() {
String sql = "SELECT * FROM orders WHERE status = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "ACTIVE");
try (ResultSet rs = ps.executeQuery()) {
List<Order> orders = new ArrayList<>();
while (rs.next()) {
orders.add(Order.fromResultSet(rs));
}
return orders;
}
} catch (SQLException e) {
log.error("DB error", e);
return Collections.emptyList();
}
}
案例3:集合使用不当
问题场景
// 错误示范:频繁扩容+错误的数据结构
public void processItems() {
// ArrayList初始容量太小,频繁扩容
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
list.add(i);
}
// 频繁查找使用List,时间复杂度O(n)
boolean exists = list.contains(999999);
// 遍历删除使用ArrayList
for (Integer num : list) {
if (num % 2 == 0) {
list.remove(num); // ConcurrentModificationException
}
}
}
解决方案
// 正确方式:合理选择数据结构
public void processItems() {
// 预估容量,避免扩容
List<Integer> list = new ArrayList<>(1000000);
for (int i = 0; i < 1000000; i++) {
list.add(i);
}
// 频繁查找使用HashSet,时间复杂度O(1)
Set<Integer> set = new HashSet<>(list);
boolean exists = set.contains(999999);
// 遍历删除使用迭代器或removeIf
list.removeIf(num -> num % 2 == 0);
// 或者使用效率更高的方式
List<Integer> filtered = list.stream()
.filter(num -> num % 2 != 0)
.collect(Collectors.toList());
}
案例4:同步过度
问题场景
// 错误示范:过度使用同步
public class Counter {
private int count = 0;
// 每个线程独立不需要同步
public synchronized void increment() {
count++;
}
// 读操作也不需要同步(如果允许短暂不一致)
public synchronized int getCount() {
return count;
}
// 整个方法同步,包含耗时的IO操作
public synchronized void processWithIO() {
doSomeIOOperation(); // 耗时操作
count++;
sendNotification(); // 外部API调用
}
}
解决方案
// 正确方式:减少锁的粒度
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
// 使用原子操作
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
private final Object lock = new Object();
// 只对需要保护的代码块加锁
public void processWithIO() {
doSomeIOOperation(); // 不需要加锁
synchronized(lock) {
// 只保护关键操作
int current = count.get();
count.set(current + 1);
}
// 异步发送通知
CompletableFuture.runAsync(this::sendNotification);
}
}
案例5:大对象导致GC问题
问题场景
// 错误示范:频繁创建大数组
public class DataProcessor {
public void processBatch() {
for (int i = 0; i < 1000; i++) {
// 每次循环创建大数组,导致频繁GC
byte[] data = new byte[10 * 1024 * 1024];
// 处理数据...
}
}
}
解决方案
// 正确方式:复用缓冲区和池化技术
public class DataProcessor {
private static final int BUFFER_SIZE = 10 * 1024 * 1024;
private final byte[] fixedBuffer = new byte[BUFFER_SIZE];
// 对象池
private static final ThreadLocal<byte[]> bufferPool =
ThreadLocal.withInitial(() -> new byte[BUFFER_SIZE]);
public void processBatch() {
for (int i = 0; i < 1000; i++) {
byte[] data = bufferPool.get();
// 注意:每次使用前需清空
Arrays.fill(data, (byte) 0);
// 处理数据...
}
}
}
案例6:Web应用性能问题
问题场景
// 错误示范:Response非常慢
@RestController
public class UserController {
@Autowired
private UserService userService;
// 1. 同步阻塞调用
// 2. 每次都查数据库
// 3. 返回大结果集
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAllUsersWithDetails();
}
// 大量重复查询
@GetMapping("/user/{id}/details")
public UserDetailView getUserDetail(@PathVariable String id) {
return userService.getUserDetailById(id); // 每次都访问DB
}
}
解决方案
@RestController
public class UserController {
@Autowired
private CacheManager cacheManager;
@Autowired
private UserService userService;
// 1. 分页查询
// 2. 使用缓存
// 3. 异步处理
@GetMapping("/users")
public Page<UserSummary> getUsers(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
String cacheKey = String.format("users:%d:%d", page, size);
Page<UserSummary> cached = cacheManager.get(cacheKey, Page.class);
if (cached != null) {
return cached;
}
Page<UserSummary> result = userService.getPage(page, size);
cacheManager.put(cacheKey, result, Duration.ofMinutes(10));
return result;
}
// 缓存热点数据
@Cacheable(value = "userDetail", key = "#id")
@GetMapping("/user/{id}/details")
public CompletableFuture<UserDetailView> getUserDetail(@PathVariable String id) {
return CompletableFuture.supplyAsync(() -> userService.getUserDetailById(id));
}
}
性能调优工具使用建议
CPU分析
# 使用jstack查看线程状态 jstack -l <pid> > thread_dump.txt # 使用VisualVM/JMC进行CPU分析 # 使用async-profiler ./profiler.sh -c cpu -d 30 -o html <pid>
内存分析
# 堆转储 jmap -dump:format=b,file=heap.hprof <pid> # 使用MAT/Eclipse MAT分析内存泄漏 # 查看GC日志 java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log
JVM参数调优示例
java -Xms4g -Xmx4g \
-XX:NewRatio=2 \
-XX:SurvivorRatio=8 \
-XX:MaxTenuringThreshold=15 \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:+DisableExplicitGC \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/path/to/dump \
-Dspring.profiles.active=prod \
-jar application.jar
这些案例展示了Java性能问题的多样性和复杂性,关键在于:
- 合理使用数据结构
- 注意资源管理
- 减少锁竞争
- 合理使用缓存
- 定期进行性能分析和调优