面试题答案
一键面试1. 实际场景:性能监控
在Java应用开发中,对方法执行时间进行性能监控是一个常见需求。例如在一个电商系统中,对于商品查询、订单处理等核心业务方法,监控其执行时间有助于发现性能瓶颈。
2. 实现思路
- 定义注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PerformanceMonitor {
}
- 字节码增强:
- 以AspectJ为例,AspectJ是一个强大的字节码增强框架。通过定义切面类,在方法执行前后记录时间,实现性能监控。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class PerformanceMonitorAspect {
@Around("@annotation(PerformanceMonitor)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long endTime = System.currentTimeMillis();
System.out.println(joinPoint.getSignature() + " 执行时间: " + (endTime - startTime) + " ms");
}
}
}
- 配置启用: 在Spring配置文件(如applicationContext.xml)中配置AspectJ切面,使得注解和切面生效。
<context:component - scan base - package="com.example"/>
<aop:aspectj - autoproxy/>
3. 高并发场景下的优化
- 减少系统开销:
- 使用线程本地变量:在性能监控时,为每个线程分配独立的时间记录变量,避免多线程竞争。例如,将记录开始时间的变量改为
ThreadLocal<Long>
类型。
- 使用线程本地变量:在性能监控时,为每个线程分配独立的时间记录变量,避免多线程竞争。例如,将记录开始时间的变量改为
private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();
@Around("@annotation(PerformanceMonitor)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
startTimeThreadLocal.set(System.currentTimeMillis());
try {
return joinPoint.proceed();
} finally {
long endTime = System.currentTimeMillis();
long startTime = startTimeThreadLocal.get();
System.out.println(joinPoint.getSignature() + " 执行时间: " + (endTime - startTime) + " ms");
startTimeThreadLocal.remove();
}
}
- 异步记录:将性能监控数据的记录操作异步化,避免阻塞核心业务逻辑。可以使用
CompletableFuture
或线程池来实现。例如:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
@Around("@annotation(PerformanceMonitor)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long endTime = System.currentTimeMillis();
executorService.submit(() -> {
System.out.println(joinPoint.getSignature() + " 执行时间: " + (endTime - startTime) + " ms");
});
}
}
-
优化资源管理:
- 连接池:如果性能监控涉及到数据库等外部资源连接,使用连接池来管理连接,避免频繁创建和销毁连接。例如使用HikariCP连接池。
- 缓存:对于一些重复计算的监控数据,可以使用缓存来提高效率。例如使用Guava Cache缓存部分监控指标。
-
稳定性保障:
- 异常处理:在字节码增强逻辑中,加强异常处理,避免因性能监控逻辑异常导致核心业务中断。
@Around("@annotation(PerformanceMonitor)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
// 记录异常,确保核心业务异常不会被掩盖
System.err.println("性能监控时捕获到异常: " + throwable.getMessage());
throw throwable;
} finally {
long endTime = System.currentTimeMillis();
System.out.println(joinPoint.getSignature() + " 执行时间: " + (endTime - startTime) + " ms");
}
}
- 监控指标聚合:在高并发下,大量的监控数据可能导致性能问题。可以采用指标聚合的方式,定期将监控数据进行汇总,减少数据量。例如,每分钟统计一次方法的平均执行时间、最大执行时间等。