十六、JVM垃圾回收机制与调优实战
垃圾收集器对比
收集器 适用场景 核心特点 触发GC类型
Serial 单线程环境 线程独占,StopTheWorld Minor GC
Parallel Scavenge 吞吐量优先 多线程并行,关注系统吞吐量 Minor/Full GC
CMS 低延迟应用 并发标记清除,碎片化问题 Concurrent Mode Failure
G1 大内存多核服务器 Region划分,可预测停顿时间 Mixed GC
java
// JVM调优参数示例
-Xms4g -Xmx4g // 避免堆内存动态调整
-XX:+UseG1GC // 启用G1收集器
-XX:MaxGCPauseMillis=200 // 目标最大停顿时间
-XX:InitiatingHeapOccupancyPercent=45 // 触发并发周期的堆占用率阈值
关键技术点
1. 内存溢出定位:通过jmap生成堆转储文件,MAT工具分析内存泄漏链
2. Full GC触发条件:老年代空间不足、System.gc()调用、元空间不足
3. GC日志分析:通过GCViewer工具解析吞吐量、停顿时间等指标
十七、分布式唯一ID生成方案对比
主流方案对比
方案 实现原理 优点 缺点
Snowflake 时间戳+机器ID+序列号 高性能,趋势递增 时钟回拨问题
Leaf 数据库号段+Redis计数 可靠性高,支持高并发 依赖外部中间件
Redis INCR 原子操作生成自增ID 实现简单 单点故障,容量限制
UUID 随机字符串生成 无中心化依赖 存储空间大,无序
java
// Snowflake算法实现
public class SnowflakeIdGenerator {
private final long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨异常");
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & 0xFFF;
if (sequence == 0) {
timestamp = waitNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - 1288834974657L) << 22)
| (workerId << 12)
| sequence;
}
}
核心挑战
1. 时钟回拨处理:记录上次生成时间,异常时抛出或等待
2. 分布式协调:ZooKeeper/Etcd分配机器ID
3. 号段预分配:数据库批量获取减少IO次数
十八、数据库分库分表策略设计
分片键选择原则
1. 高频查询字段:如用户ID、订单ID
2. 均匀分布:避免数据倾斜(如哈希分片)
3. 业务无关性:减少跨分片查询
sql
-- 用户表按ID取模分片
CREATE TABLE user_0 (
id BIGINT PRIMARY KEY,
name VARCHAR(20)
) SHARDKEY=id;
CREATE TABLE user_1 (
id BIGINT PRIMARY KEY,
name VARCHAR(20)
) SHARDKEY=id;
跨分片查询方案
1. 全局表:冗余存储只读的维度表
2. 映射表:记录分片键与物理表的对应关系
3. 并行查询:对多个分片发起并行查询后合并
技术难点
1. 分布式事务:XA/TCC补偿机制
2. 数据迁移:双写+灰度迁移策略
3. 分片扩容:一致性哈希减少数据迁移量
十九、限流算法实现与对比
核心算法实现
java
// 令牌桶算法实现
public class TokenBucket {
private final long capacity; // 桶容量
private final double refillRate; // 令牌填充速率(个/秒)
private long tokens; // 当前令牌数
private long lastRefillTimestamp; // 上次填充时间戳
public synchronized boolean tryAcquire(int permits) {
refill();
if (tokens >= permits) {
tokens -= permits;
return true;
}
return false;
}
private void refill() {
long now = System.currentTimeMillis();
long delta = now - lastRefillTimestamp;
long refillAmount = (delta * refillRate) / 1000;
if (refillAmount > 0) {
tokens = Math.min(capacity, tokens + refillAmount);
lastRefillTimestamp = now;
}
}
}
// 漏桶算法实现
public class LeakyBucket {
private final long capacity; // 桶容量
private long water; // 当前水量
private final double outflowRate; // 流出速率(个/秒)
private long lastLeakTimestamp; // 上次漏水时间戳
public synchronized boolean tryAcquire() {
leak();
if (water < capacity) {
water++;
return true;
}
return false;
}
private void leak() {
long now = System.currentTimeMillis();
long delta = now - lastLeakTimestamp;
long leakAmount = (delta * outflowRate) / 1000;
if (leakAmount > 0) {
water = Math.max(0, water - leakAmount);
lastLeakTimestamp = now;
}
}
}
方案选型建议
场景 推荐算法 核心优势
突发流量允许 令牌桶 平滑流量,应对突发
严格限制请求速率 漏桶 严格固定速率输出
多级限流 滑动窗口日志 精确控制时间窗口内请求数
二十、Spring Bean生命周期与循环依赖
Bean加载全流程
mermaid
sequenceDiagram
容器启动->>Bean定义加载: 解析XML/注解
Bean定义加载->>实例化: 创建Bean对象
实例化->>属性填充: 注入依赖Bean
属性填充->>初始化: 执行Aware接口/AOP代理
初始化->>使用: 可被应用程序使用
使用->>销毁: 容器关闭时调用@PreDestroy
循环依赖解决方案
1. 构造器注入:无法解决,必须避免
2. Setter注入:三级缓存提前暴露Bean引用
3. @Lazy延迟加载:打破初始化链式调用
java
// 循环依赖示例
@Service
class AService {
@Autowired
private BService bService;
}
@Service
class BService {
@Autowired
private AService aService;
}
源码解析
1. createBeanInstance:通过反射/工厂方法创建Bean
2. populateBean:处理@Autowired/@Resource注解
3. initializeBean:执行InitializingBean接口方法