底层源码面试常问问题?

访客 源码剖析 1

从JVM到并发编程的核心剖析

目录导读

  • JVM底层原理篇:内存模型、垃圾回收与类加载机制
  • 并发编程底层篇:synchronized、volatile与AQS源码解析
  • 集合框架底层篇:HashMap、ConcurrentHashMap的红黑树与扩容
  • 网络与IO底层篇:BIO/NIO/AIO与Reactor模式
  • 常见面试问答集锦:高频问题深度解答

JVM底层原理篇

JVM内存模型与对象创建过程

面试官常问:“一个对象从加载到使用,JVM内部发生了什么?” 核心在于理解类加载机制对象内存布局,类加载通过双亲委派模型,从加载、验证、准备、解析到初始化,对象创建则需经过:检查类是否加载、分配内存(指针碰撞或空闲列表)、初始化零值、设置对象头、执行init方法。

问答环节

  • :对象头包含哪些信息?
    :Mark Word(存储锁状态、GC分代年龄、hashCode等)、类型指针(指向类元数据)、数组长度(如果是数组)。

垃圾回收算法与CMS/G1调优

面试必问:可达性分析的GC Roots有哪些?三色标记如何解决漏标?CMS的并发标记阶段如何工作?G1的Region停顿预测模型如何实现可控停顿?

核心点

  • 新生代:复制算法(Minor GC)
  • 老年代:标记-清除/标记-整理(CMS、G1)
  • 调优参数:-Xms、-Xmx、-XX:NewRatio、-XX:+UseG1GC等

问答环节

  • :为什么CMS会出现Concurrent Mode Failure?
    :当老年代空间不足,且后台线程回收速度跟不上分配速度时,会触发Serial Old进行单线程Full GC。

并发编程底层篇

synchronized的锁升级过程

无锁 → 偏向锁 → 轻量级锁 → 重量级锁,每个阶段的触发条件与Mark Word变化是高频考点,Java 6后通过锁消除锁粗化优化性能。

核心源码:偏向锁通过CAS在对象头存储线程ID;轻量级锁通过自旋;重量级锁依赖OS互斥量(Mutex)。

问答环节

  • :偏向锁为什么在JDK 15被默认禁用?
    :因高并发场景下撤销偏向锁的成本高,且现代应用多使用轻量级锁,默认禁用可减少JIT优化复杂度。

volatile与JMM的happens-before

volatile保证可见性(通过Lock前缀指令刷新缓存)和有序性(禁止指令重排序),但不保证原子性,面试常结合双重检查锁(DCL) 问为什么需要volatile。

问答环节

  • :volatile能替代synchronized吗?
    :不能,volatile只解决读写可见性和有序性,但不保证复合操作(如i++)的原子性。

AQS的实现原理与ReentrantLock

AQS(AbstractQueuedSynchronizer)通过CLH队列(FIFO双向链表)和状态变量state实现同步,ReentrantLock的公平/非公平体现在tryAcquire中:非公平直接CAS;公平检查队列是否有前任。

核心源码

  • tryAcquire:CAS设置state,exclusiveOwnerThread
  • acquireQueued:线程在队列中自旋或park

问答环节

  • :CountDownLatch与CyclicBarrier底层实现有何不同?
    :CountDownLatch基于AQS的共享模式,state为计数器;CyclicBarrier基于ReentrantLock和Condition,支持循环使用。

集合框架底层篇

HashMap的put与get源码解析

8后采用数组+链表+红黑树,扩容机制:默认0.75负载因子,达到阈值时resize(2倍扩容),链表长度>8且数组长度≥64转红黑树。

问答环节

  • :为什么HashMap的key需重写hashCode和equals?
    :hashCode决定桶位置,equals判断键是否相同;避免哈希冲突导致覆盖或查询失败。

ConcurrentHashMap的线程安全实现

7使用分段锁(Segment数组),8使用CAS+synchronized(对每个Node桶加锁),扩容时通过transfer方法让多线程并行迁移。

问答环节

  • :ConcurrentHashMap的size()方法如何统计?
    :通过CounterCell数组累加,使用LongAdder思想减少CAS竞争。

网络与IO底层篇

BIO/NIO/AIO的区别与Reactor模型

BIO阻塞模式,NIO通过Selector实现多路复用,AIO基于回调/通知。Reactor模型(单Reactor单线程、多线程、主从多线程)是Netty核心,通过事件驱动处理连接和IO。

问答环节

  • :NIO的epoll为什么比select高效?
    :select需遍历全量fd并每次拷贝到内核;epoll通过红黑树注册事件,只返回就绪fd,且使用mmap减少拷贝。

常见面试问答集锦(深度Q&A)

Q1:JVM年轻代为什么分为Eden和两个Survivor区?

:基于分代收集标记-复制算法,Eden存放新对象,存活对象复制到S0/S1,默认比例8:1:1,减少内存碎片,提升Minor GC效率。

Q2:ThreadLocal的内存泄漏如何产生?如何避免?

:ThreadLocalMap的Key是弱引用,但Value是强引用,若ThreadLocal被GC回收,Entry存在Key=null但Value有效,导致内存泄漏。避免:每次使用后调用remove();或使用静态内部类封装。

Q3:Redis与MySQL双写一致性为什么要用延时双删?

:先更新数据库,再删除缓存,但并发下存在旧数据读入缓存风险,延时双删(先删缓存,再删一次)通过短暂延迟保证最终一致,且配合缓存过期时间兜底。

Q4:分布式锁用Redis还是Zookeeper?底层差异?

:Redis基于SETNX+过期时间(需原子性),存在主从切换锁丢失风险;Zookeeper基于临时顺序节点+监听,强一致性但性能略低,选择取决于业务对一致性与性能的要求。


底层源码面试不仅考察记忆,更考验原理理解场景联想,建议从JVM、并发、集合三大核心模块入手,结合源码片段(如HashMap的红黑树平衡、AQS的CLH队列入队)进行思维导图整理,面试时,回答前先问清场景,然后分层解析:为什么这样设计 → 解决了哪些问题 → 遇到过什么坑,掌握这些,面试官自然会对你的底层功底刮目相看。

如果您希望针对特定题目(如“怎么设计一个MQ”或“Redis持久化原理”)展开深度解析,欢迎留言讨论。

标签: 垃圾回收机制

抱歉,评论功能暂时关闭!