Java面试高频问题(16-20)

十六、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接口方法

 

 

你可能感兴趣的:(java,面试,算法)