2026.4.28 d6
1. HashMap 的底层实现原理
JDK 1.8 之后:
- 数据结构:数组 + 链表 + 红黑树
- 数组:Node<K,V>[] table
- 每个位置叫 桶(bucket)
流程:
- key → hash
- hash → (n - 1) & hash 定位数组下标
- 发生冲突:
- 链表(长度 < 8)
- 红黑树(长度 ≥ 8 且数组长度 ≥ 64) 关键点:
- 负载因子(loadFactor)默认 0.75
- 扩容:容量 × 2
- 链表转红黑树阈值:8
- 红黑树转链表阈值:6
2. put(k, v) 的流程
▼text复制代码put(key, value) ↓ 计算 hash(key) ↓ index = (n - 1) & hash ↓ 判断 table[index] 是否为空 ↓ 为空 → 直接插入 ↓ 不为空 ↓ 判断是否 key 相同 ↓ 相同 → 覆盖 value ↓ 不同 → 插入链表 / 红黑树 ↓ 链表长度 ≥ 8 → 转红黑树 ↓ size++ ↓ 是否超过阈值 threshold ↓ 超过 → resize()
和 HashMap 的线程安全集合
ConcurrentHashMap
- JDK1.7:Segment 分段锁
- JDK1.8:CAS + synchronized,桶为空使用CAS操作,不为空加锁操作 Hashtable
- 方法级 synchronized
- 粗粒度锁,性能差 Collections.synchronizedMap
- 本质:包装 HashMap
- 整体加锁
AQS和ReentrantLock
AQS:Java 并发包里的一个同步器框架。 AQS = state 状态变量 + CLH 等待队列 + CAS 修改状态
▼text复制代码AQS ├── state:同步状态 │ └── int 类型,表示锁是否被占用、资源数量等 │ ├── CAS:修改 state │ └── 保证并发安全 │ └── 等待队列 └── 获取锁失败的线程进入队列等待
线程获取锁时,会先通过 CAS 修改 state,如果失败就进入队列排队,并通过 LockSupport.park 阻塞;释放锁时通过 unpark 唤醒后继节点。
AQS 使用模板方法模式,将 tryAcquire / tryRelease 等方法交给子类实现,从而支持 ReentrantLock、Semaphore、CountDownLatch 等不同同步器。
synchronized 锁升级流程
锁状态(从低到高)
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
升级过程
- 无锁
- 对象刚创建,没有竞争
- 偏向锁
- 第一个线程进入,记录线程ID到对象头
- 后续该线程进入不需要加锁(无CAS)
- 适用于“单线程反复进入”
- 轻量级锁
- 多线程竞争,但不激烈
- 通过 CAS + 自旋 尝试获取锁
- 避免线程阻塞
- 重量级锁
- 自旋失败 or 竞争激烈
- 升级为 OS 互斥锁(mutex)
- 线程阻塞 + 上下文切换
- 锁 只能升级,不能降级
- 目的:减少用户态 → 内核态切换
MySQL 优化方向
1. SQL 层
- 避免
select * - 避免函数、计算导致索引失效
- 使用覆盖索引
2. 索引层
- 合理设计 联合索引(最左匹配)
- 避免索引失效(隐式类型转换等)
3. 架构层
- 读写分离
- 分库分表
- 缓存(Redis)
分库分表
垂直拆分(Vertical)
按“字段 ”拆
例:
user表 → user_basic + user_detail
特点:
- 减少单表字段数
- 提高缓存命中率
水平拆分(Horizontal)
👉 按“数据行”拆
例:
user_0, user_1, user_2 (按 user_id % 3)
特点:
- 解决数据量过大
- 提高并发
三、MySQL 事务隔离级别
四种隔离级别
| 隔离级别 | 能解决的问题 |
|---|---|
| 读未提交(RU) | 无 |
| 读已提交(RC) | 脏读 |
| 可重复读(RR) | 脏读 + 不可重复读 |
| 串行化(Serializable) | 脏读 + 不可重复读 + 幻读 |
- 脏读:读到未提交数据
- 不可重复读:查询同一行数据多次前后不一致
- 幻读:相同的范围查询结果
Redis 持久化机制
RDB(快照)
- 定时生成 dump.rdb
- fork 子进程 + COW(写时复制) 优点:
- 文件小,恢复快 缺点:
- 数据可能丢失
AOF(日志)
- 记录每次写操作 策略:
- always(每次刷盘)
- everysec(默认)
- no(操作系统决定) 优点:
- 数据更安全 缺点:
- 文件大
混合持久化
- RDB + AOF Redis 混合持久化是在 AOF rewrite 时,先生成 RDB 快照作为基础数据,再追加 AOF 增量日志。
恢复时先加载 RDB,再重放 AOF,兼顾恢复速度和数据安全性。
Redis 内存淘汰策略
不淘汰
- noeviction
TTL相关
- volatile-lru
- volatile-lfu
- volatile-random
- volatile-ttl
全局淘汰
- allkeys-lru
- allkeys-lfu
- allkeys-random
六、JVM 内存结构
▼text复制代码JVM ├── 堆(Heap) │ ├── 新生代(Eden + S0 + S1) │ └── 老年代 ├── 方法区(MetaSpace) ├── 虚拟机栈(Java Stack) ├── 本地方法栈 └── 程序计数器
各区域作用
- 堆
- 存对象
- GC主要区域
- 方法区(MetaSpace)
- 类信息、常量
- 虚拟机栈
- 方法调用、局部变量
- 程序计数器
- 记录当前执行字节码文件位置,方便线程切换后找到执行位置
常见垃圾回收算法
标记-清除
- 标记:标记所有存活对象
- 清除:回收未标记对象
复制算法
- 将内存分为两块(from / to,年轻代的S0和S1)
- 存活对象复制到另一块
- 清空原区域
标记-整理
- 标记存活对象
- 将存活对象向一端移动
- 清理边界外内存
常见收集器
- Serial
- ParNew
- CMS
- G1 把堆切成大小相等的 Region,每个 Region 在运行时动态充当 Eden / Survivor / Old之一,GC 优先回收性价比最高(性价比 = 可释放空间 / 预估回收耗时)的 Region,兼顾吞吐与延迟。
- ZGC
用着色指针在对象引用里存元数据,配合读屏障,让几乎所有 GC 工作都与应用并发执行,STW 只剩根扫描的极短暂停。
ZGC着色指针这样存:
reference → [状态位 | 对象地址]
传统GC时,移动对象时所有引用都需要更新,着色指针的好处是把对象的状态信息编码进引用中,使得 JVM 在访问对象时可以通过读屏障判断引用是否需要修正。
▼text复制代码应用线程读取对象引用 ↓ 触发读屏障 ↓ 检查指针上的“颜色” ↓ 判断对象是否已移动 ↓ 如果移动 → 修正为新地址 ↓ 继续执行

面经链接
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
内容推荐
我想问一下大家简历的字体是多少号的呢,是2页还是3页
2
今天面了两家公司,面试时间都比较短直接总结到一起了第一家1. 自我介绍2. 公司两个项目开发中,用到 Codex 或 Claude Code 了吗?3. 具体是用来写什么?用的是最多的哪个?4. Claude Code 用的是什么模型?5. 什么时候开始用的?(精确到具体月份)6. 是官方账号还是中转站?7. 当时开发一天用多少钱?8. 平时用到什么 Skill 或 MCP 吗?9. Claude
5
找实习从7月22号开始投,现在也总算是有offer了
5
怎么会有笨蛋从一月到现在,背了7个月的面试题,还啥都不会呢,到底背哪里去了,该怎么办
3
【入职求助贴】萌新刚入职某大厂做后端开发,目前还在试用期。最近遇到一个棘手的问题,想向大家求助一下。入职不久,leader 给我派了一个任务。跟我说是0.5天就可以解决,我刚毕业入职,做了一个星期没有做出来。实现一个收集定时成功任务的案例。听起来好像不复杂,但我自己摸索着做了一整个星期,到现在还没达到预期效果。这一周我基本是“边学边做”的状态,遇到卡点也会每天主动找 leader 沟通进度和疑问。
2
