面灵AI

携程后端面经合集:HashMap、缓存一致性与 JVM 排查

时间
2026-09
来源
牛客网

《面试题目》

2025 年 9 月 21 日至 22 日:集合、泛型与限流

  1. HashMap 为什么在链表较长时转成红黑树,树化需要哪些条件?
  2. MVCC 能解决哪些并发读取问题,快照读与当前读混用时要注意什么?
  3. 深分页查询怎样优化,多级缓存怎样设计?
  4. 线程池达到核心线程数后,新任务怎样处理?
  5. Java 泛型如何实现,TypeReference 怎样保留并解析类型信息?
  6. 泛型通配符 extends 与 super 分别适合什么场景?
  7. CMS 与 G1 的适用场景、算法和执行流程有什么区别?
  8. Java 常用集合有哪些,HashMap 的 put 完整流程是什么?
  9. 红黑树怎样查找,为什么能控制查询复杂度?
  10. MySQL 四个隔离级别分别处理什么问题,幻读和不可重复读有什么区别?
  11. Redis 有哪些数据类型,热 Key 和大 Key 如何处理?
  12. Redis Cluster 查询怎样路由?
  13. 怎样实现本地按 IP 限流,要求一秒内超过 10 次访问返回 false?
  14. 对带嵌套的 k[encoded_string] 字符串,如何展开重复内容?
  15. RAG、Lua 脚本、全局唯一编号和 OpenFeign 分别怎样用在项目里?
  16. Spring Boot 自动配置怎样生效?
  17. 线程池使用 ThreadLocal 时有哪些内存风险,怎样清理?
  18. 两阶段提交与三阶段提交有什么区别?
  19. HTTP 和 TCP 有什么关系?
  20. ReentrantLock 与 synchronized 有哪些区别?

2025 年 9 月 18 日至 19 日:项目与消息系统

  1. 项目主要开发什么功能,开发和测试的分工是什么?
  2. 两个模块怎样通信,Redis 在其中用来做什么?
  3. ZSet 底层怎样组织数据,Redis 怎样判断和清理过期键?
  4. equals 与 hashCode 有什么关系?
  5. 除 new 以外,还有哪些创建对象的途径?
  6. JSON 序列化与反序列化怎样工作,反序列化有哪些风险?
  7. 线程同步有哪些方式,CAS 有哪些局限?
  8. 如何了解 JDK 版本变化,遇到过哪些 OOM?
  9. 接口幂等、消息可靠性和消息顺序分别怎样保证?
  10. 数组与链表有什么区别,JDK 8 的 HashMap 有哪些变化?
  11. 什么是内存访问的有序性,怎样建立线程间的顺序约束?
  12. 能否用 String 对象加锁,有什么风险?
  13. 对象创建、类加载与双亲委派分别经过哪些过程?
  14. 可重复读与 MVCC 有什么关系?
  15. SELECT * 可能带来哪些额外成本?
  16. 用哈希表保存百万条数据有什么优点与代价?
  17. Redis Hash 与 Java HashMap 有哪些差异?
  18. 面向用户请求的业务线程池怎样设置参数?
  19. Reactor I/O 模型是什么?
  20. 最有收获的项目是什么,怎样解决困难和保护数据库数据?
  21. 怎样使用辅助编程工具,最近在学习什么?
  22. 遇到死锁时如何排查,如何避免重复发生?
  23. 用过哪些设计模式,单例的常见实现有什么区别?
  24. JVM 堆和线程栈各保存什么,和 Java 内存模型有什么区别?
  25. Redis List、Set、Sorted Set 分别适合什么场景?
  26. 如何自定义 Spring Boot 自动配置?

2025 年 9 月 15 日至 16 日:锁、缓存与分库分表

  1. 乐观锁与悲观锁有什么区别?
  2. 怎样基于 AQS 实现可重入的公平锁?
  3. 如果设计 Tomcat 的线程池,需要考虑哪些因素?
  4. 消息队列怎样避免重复消费,延迟消息怎样使用?
  5. 缓存穿透和击穿有什么区别,各有哪些处理办法?
  6. 布隆过滤器怎样工作,有哪些局限?
  7. 更新数据库与删除缓存不是原子操作时,怎样处理一致性?
  8. 分布式编号有哪些生成方式?
  9. binlog 有什么作用?
  10. 分库分表的依据是什么,为什么不能只根据行数作决定?
  11. 项目使用什么垃圾回收器,选择理由是什么?
  12. G1 的 Region 有哪些角色,Region 大小怎样确定?
  13. 对 ZGC 有哪些了解?
  14. 线程池有哪些拒绝策略,业务中为什么选择其中一种?
  15. 父进程、子进程和孙进程共用一个线程池,会遇到什么问题?

2025 年 9 月 13 日及更早场次:线上排查与业务设计

  1. SQL 已使用索引却依然很慢,可能有哪些原因?
  2. B+ 树有哪些优势和代价?
  3. 限流怎样实现,怎样进行压力测试?
  4. Elasticsearch 的数据存储与检索结构是什么?
  5. volatile 起什么作用,为什么会有指令重排序?
  6. 除双重检查锁外,有哪些更简单的单例实现?
  7. 对象是否一定分配在堆上,对象头保存哪些信息?
  8. 怎样理解 synchronized 的锁状态变化?
  9. 生产环境 CPU 飙高时怎样排查?
  10. 项目中的业务目标和技术选择怎样权衡?
  11. 秒杀系统怎样设计,数据库层面如何避免超卖?
  12. Full GC 可能由什么触发,怎样分析?
  13. 水平分表后还需要解决哪些问题?
  14. 接口自动化验证除了检查响应报文,还能检查什么?
  15. 哈希算法和 HashMap 的工作方式是什么?
  16. CMS 与 G1 的回收过程有什么区别?

《参考解析》

HashMap 的阈值不是“8 个元素一定树化”

JDK 8 的实现还会检查数组容量。容量较小时优先扩容,让哈希冲突有机会分散;满足容量条件后才考虑树化。树节点有额外空间成本,因此短链表不值得付出这份开销。树化、拆分时退化使用不同阈值,也减少了形态频繁切换。回答时应把链表长度、数组容量和扩容过程一起讲清。

字符串解码用栈保存外层状态

逐字符扫描,连续数字组成重复次数;遇到左括号时,把当前字符串与次数入栈,并开始新的内层片段;遇到右括号时,取回外层字符串,把内层内容重复指定次数后拼接回去。需要处理多位数和多层嵌套。复杂度还受展开后输出长度影响,不能只按输入长度计算。

严格的一秒限流先明确窗口

如果要求任意连续一秒最多放行 10 次,可为每个 IP 保存已通过请求的时间队列。请求到达时移除窗口外的时间戳,剩余不足 10 个才放行并入队。清理、判断和入队必须作为一个受保护的操作。固定自然秒计数实现更简单,但在秒边界附近可能短时间通过两倍额度,它不等价于滑动窗口。

缓存一致性不能只背“双删”

先明确允许读到旧值多久。常见流程是事务提交后删缓存,删除失败进入可重试记录;但旧查询也可能在删除后把旧值写回缓存。可根据业务采用版本号校验、串行化同一键的更新、变更订阅和过期时间等办法约束这个窗口。延迟再删一次只是缩小特定竞态,不自动等于强一致。

索引命中不是优化结束

检查实际扫描行数、返回行数和回表次数,再看排序、临时结果、锁等待与网络返回量。一个条件能用索引,不代表选择性足够高,也不代表覆盖排序和所需列。不要为了让执行计划显示某个索引,就忽略整条查询的时间和资源消耗。

内存术语别混在一起

JVM 运行时区域描述堆、栈等存储组织;Java 内存模型讨论线程之间的可见性、原子性和先行发生关系。对象引用作为局部变量时可以位于线程栈,对象通常分配在堆。JIT 可能通过逃逸分析和标量替换消除某些分配,因此“局部对象一定完整地放在栈上”也不准确。

TypeReference 保留的是类型声明信息

普通泛型对象不能直接告诉你运行时的全部类型参数,但匿名子类可以在泛型父类签名中保留诸如 List 的声明。TypeReference 一类工具通过反射读取这些信息,再交给反序列化器使用。如果写成 TypeReference,而 T 在该处仍是未解析的类型变量,也不会自动变成具体业务类型。

布隆过滤器不适合随意清位

多个元素可能映射到相同的位。查询命中的元素可能并不存在,但正常插入且未损坏、未误删的过滤器不会漏掉已经插入的元素。删除一个元素时直接清位,会误伤其他元素;要支持过期,可考虑分时间窗口轮换过滤器,或使用带计数的变体,并明确内存和并发代价。

进程不能直接共用一个 Java 线程池对象

不同进程有独立地址空间。若题目实际指父任务提交子任务,那么重点是线程池饥饿:工作线程全部阻塞等待子任务,而子任务又排在同一个池里,就可能无人执行。若确实是跨进程协作,应讨论进程间通信和任务分发,而不是把普通线程池当作共享对象。