跳到主要内容

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分区 RegionJDK 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:在线分析热点方法和内存异常