Java / Spring Boot 核心面经
Java 运行时 & Spring Boot 深度
1. JVM 内存结构
Heap(堆)
Young Generation
Eden Space <- 新对象分配在这里
Survivor S0/S1 <- 经历多次 GC 的对象
Old Generation <- 长期存活的对象
Non-Heap
Metaspace <- 类元数据(JDK 8+ 替换 PermGen)
Code Cache <- JIT 编译的机器码
Stack(每个线程一个)
方法调用栈帧:局部变量、返回地址、操数栈
2. GC 算法
| 收集器 | 算法 | 适用场景 |
|---|---|---|
| Serial GC | 单线程标记-清除 | 单核/内存小 |
| Parallel GC | 多线程吸层 | 吸层优先 |
| CMS | 并发标记-清除 | 低延迟(已废弃) |
| G1 GC | 分区 Region | JDK 9+ 默认 |
| ZGC | 全并发、每次 < 1ms STW | 超大堆/低延迟 |
G1 GC 工作原理
- 将堆划分为大小相等的 Region(2MB)
- 优先回收垃圾最多的 Region(Garbage First)
- 可预测停顿时间,默认目标 200ms
3. Java 并发深度
volatile 关键字
private volatile boolean flag = false;
// 保证可见性:一个线程修改后其他线程立即可见
// 保证有序性:Happens-Before 规则
// 不保证原子性:count++ 不是原子操作
synchronized vs ReentrantLock
// synchronized:简单、JVM 自动管理
synchronized (lock) {
// 临界区
}
// ReentrantLock:可中断、公平锁、多条件
ReentrantLock lock = new ReentrantLock(true); // 公平锁
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
// 临界区
}
} finally {
lock.unlock(); // 必须手动释放
}
// 多条件锁实现生产者消费者
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
ConcurrentHashMap 实现(JDK 8+)
- 数组 + 链表 + 红黑树
- 不再使用分段锁,转而用 CAS + synchronized
- 每个桶用 synchronized 魔谁头节点
- 读操作岆负极小,岅不锁
4. Spring Boot 核心
Bean 生命周期
容器启动
-> 扫描 @Component/@Service/@Repository
-> BeanDefinition 注册
-> 实例化(构造器注入)
-> 属性注入(@Autowired)
-> Aware 接口回调
-> BeanPostProcessor#beforeInit
-> @PostConstruct
-> InitializingBean#afterPropertiesSet
-> BeanPostProcessor#afterInit’s
-> [Bean 处于就绪状态]
-> @PreDestroy -> DisposableBean#destroy
Spring AOP 实现
@Aspect
@Component
public class TimingAspect {
@Around("@annotation(Timed)")
public Object timeMethod(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long elapsed = System.currentTimeMillis() - start;
log.info("{} took {}ms", pjp.getSignature(), elapsed);
}
}
}
// 实现数据库事务也是通过 AOP 注入事务逻辑
@Transactional // 内部用 CGLIB 实现
@Transactional 常见坏呓
// 1. 同一类内方法自调失效(代理类内部调用)
@Service
public class OrderService {
@Transactional
public void createOrder() {
this.processPayment(); // 事务失效!
}
@Transactional
public void processPayment() { ... }
}
// 解决:注入自身代理
ApplicationContext.getBean(OrderService.class).processPayment();
// 2. 异常类型错误导致不回滚
@Transactional // 默认只回滚 RuntimeException
public void method() throws Exception {
// Exception 不会回滚
}
// 解决
@Transactional(rollbackFor = Exception.class)
5. Spring Cloud 微服务
服务注册: Nacos / Eureka
负载均衡: Spring Cloud LoadBalancer
API 网关: Spring Cloud Gateway
服务调用: OpenFeign
熔断: Resilience4j
配置中心: Nacos Config
链路追踪: SkyWalking / Micrometer Tracing
OpenFeign 示例
@FeignClient(name = "user-service", fallback = UserServiceFallback.class)
public interface UserServiceClient {
@GetMapping("/users/{id}")
UserDTO getUser(@PathVariable String id);
}
@Component
class UserServiceFallback implements UserServiceClient {
@Override
public UserDTO getUser(String id) {
return UserDTO.empty(); // 降级处理
}
}
6. 性能调优
# JVM 参数调优
java -Xms2g -Xmx2g # 初始和最大堆内存
java -XX:+UseG1GC # 使用 G1 GC
java -XX:MaxGCPauseMillis=200 # GC 停顿目标
# 性能分析工具
arthas # 阿里巴巴开源,线上诊断
jvisualvm / JFR # JVM 性能图谱
# 情境问题快速定位
jstack <pid> # 查看线程堵堵
jmap -heap <pid> # 堆内存分布
7. 资深面试题
- HashMap 为什么容量是 2 的幂次?
- 取模可用位运算代替:
hash & (n-1)效率高于hash % n - 2 的幂次时
n-1的二进制全是 1,哈希分布更均匀
- 取模可用位运算代替:
- Spring 的三级缓存如何解决循环依赖?
- 一级缓存:完整对象
- 二级缓存:早期暴露的对象(尚未设置属性)
- 三级缓存:ObjectFactory(创建对象的工厂)
- 构造器注入的循环依赖不能解决
- @Transactional 与 ThreadLocal 的关系?
- Spring 事务的连接和事务状态绑定在 ThreadLocal 中
- 异步线程中事务无法传播,需特殊处理
- JVM 调优第一步看什么?
- 先确认是 CPU、内存还是 GC 问题
- GC 日志:分析停顿时间和频率
- Arthas:在线分析热点方法和内存异常