开场说明这是一场模拟 30 分钟左右的 Java 大厂一面目标候选人为 1-3 年经验的 Java 后端工程师或高阶校招生。面试节奏紧凑问题覆盖 Java 基础、并发、JVM、数据库、缓存及真实项目场景重点考察候选人对技术原理的理解深度、边界处理能力以及线上问题应对经验。整场面试以“拷打感”贯穿强调连续追问和逻辑递进避免浅层知识堆砌。主问题部分1. 你说你熟悉 JVM那请解释一下对象创建的过程从 new 关键字到对象在堆中分配中间经历了哪些步骤参考回答对象创建从new指令开始JVM 首先检查常量池中是否有该类的符号引用并执行类加载若未加载。接着在堆中分配内存分配方式取决于 GC 算法串行 GC 使用指针碰撞G1 或 CMS 使用空闲列表。分配完成后JVM 会将对象头Mark Word、Klass Pointer和实例数据区域清零不包括数组长度。然后执行init方法进行字段初始化。追问点预埋对象逃逸、TLAB、指针碰撞 vs 空闲列表。2. 在并发环境下创建对象JVM 如何保证线程安全比如多个线程同时 new 同一个类。参考回答JVM 通过 CAS 失败重试机制保证内存分配的线程安全。此外每个线程有独立的 TLABThread-Local Allocation Buffer在 Eden 区划分出一小块私有内存用于对象分配避免频繁竞争全局锁。只有当 TLAB 用满或对象过大时才需要进入慢速路径进行同步分配。3. 你提到了 TLAB那如果对象太大放不下 TLAB 怎么办JVM 如何处理这种“大对象”参考回答当对象大小超过 TLAB 剩余空间或超过-XX:PretenureSizeThreshold阈值时JVM 会直接在老年代分配前提是启用了该参数。对于 G1大对象会被分配到 Humongous Region。这种策略避免了年轻代频繁 GC但也可能导致老年代碎片化。追问点预埋Humongous Region、老年代碎片、G1 的 Region 设计。4. 你说你在项目中用过 ConcurrentHashMap那它 1.8 和 1.7 的实现有什么区别为什么 1.8 要改参考回答1.7 使用分段锁Segment每个 Segment 是一个小的 HashEntry 数组并发度由 segment 数量决定。1.8 改为 CAS synchronized 实现锁粒度细化到每个桶bucket并发度更高。同时引入红黑树优化链表过长问题查找时间复杂度从 O(n) 降到 O(log n)。5. 那在 1.8 中synchronized 锁的是谁什么时候会升级为重量级锁参考回答synchronized 锁的是每个桶的头节点Node。如果发生哈希冲突多个线程会竞争同一个头节点的锁。当竞争激烈时锁会经历偏向锁 → 轻量级锁 → 重量级锁的升级过程。升级为重量级锁后线程会进入内核态阻塞性能下降明显。追问点预埋锁升级过程、偏向锁禁用场景、实际项目中如何避免锁竞争。6. 你提到项目中用了 Redis 做缓存那你们是怎么处理缓存穿透的有没有考虑布隆过滤器参考回答我们采用布隆过滤器 空值缓存双重策略。布隆过滤器在应用启动时加载所有有效 key 的哈希位查询前先过过滤。对于未命中的 key再查 Redis若 Redis 也无数据则缓存一个空值带短过期时间防止重复击穿数据库。7. 布隆过滤器误判率怎么控制如果数据量持续增长你们怎么扩容参考回答误判率由位数组大小和哈希函数数量决定公式为(1 - e^(-kn/m))^k。我们通过预估数据量设置初始参数。对于扩容我们采用“双层布隆过滤器”策略旧过滤器只读新过滤器动态写入查询时两层都查直到旧数据过期。追问点预埋误判率计算、动态扩容方案、RedisBloom 模块使用。8. 你们项目中有分库分表吗如果有你们是怎么做路由的比如用户订单按 user_id 分 16 库 16 表。参考回答是的我们按 user_id 做哈希取模路由。具体是hash(user_id) % 256其中 256 16 库 × 16 表。每个库内再按表序号定位。路由逻辑封装在 DAO 层通过 ThreadLocal 或上下文传递分片信息。9. 那如果 user_id 是字符串怎么办你们怎么保证哈希均匀有没有遇到过哈希倾斜问题参考回答我们对字符串 user_id 使用 MurmurHash 或 CityHash避免 Java 默认 hashCode 的局部性偏差。上线前做过压测发现某些用户如内部测试账号集中在一个分片我们通过“虚拟节点”或“一致性哈希”做二次映射缓解倾斜。追问点预埋一致性哈希 vs 取模、虚拟节点实现、热点用户处理。10. 分库分表后跨分片查询怎么处理比如要查某个商品被哪些用户购买过。参考回答这类反向查询我们不走主库而是通过 ES 做二级索引。订单写入时同步一条消息到 Kafka由消费者构建商品 → 用户列表的倒排索引。查询时走 ES牺牲一点实时性换取查询能力。追问部分重点技术点深挖追问 1对象逃逸分析面试官你前面提到对象创建那有没有想过有些对象其实根本不需要在堆上分配比如一个方法内 new 了一个对象只传给局部变量使用JVM 会怎么优化候选人这是对象逃逸分析。如果 JVM 判断对象未逃逸出方法作用域即不会被其他线程访问或返回就可能触发栈上分配或标量替换直接在栈帧中分配字段避免 GC 压力。面试官那什么情况下对象会逃逸比如你把对象传给了另一个线程或者赋值给了静态变量候选人是的只要对象可能被外部访问就算逃逸。比如static List list new ArrayList();或threadLocal.set(obj)都会导致逃逸无法栈上分配。面试官那你们项目中有意识地避免对象逃逸吗比如在循环里 new 对象候选人我们会尽量避免在热点循环中创建临时对象比如用对象池或复用 StringBuilder。但 JVM 的逃逸分析默认开启很多时候已经自动优化了我们更关注显式的大对象或集合操作。追问 2ConcurrentHashMap 的 size() 方法面试官你说 ConcurrentHashMap 性能好那它的 size() 方法是怎么实现的会不会加锁候选人1.8 的 size() 是无锁的它通过 baseCount CounterCell 数组统计。每次 put/remove 会更新 baseCount高并发时通过 LongAdder 机制分散到多个 CounterCell最后求和。面试官那如果正好在统计过程中有并发修改size() 返回的是精确值吗候选人不是精确值是近似值。这是为了性能牺牲一致性。如果需要精确值可以加锁或使用mappingCount()方法返回 long但也不保证绝对实时。面试官那你们项目中需要精确 size 吗比如做限流或监控候选人我们一般用 Redis 的 INCR 做精确计数ConcurrentHashMap 的 size 只用于日志打印或调试不用于业务逻辑。追问 3分库分表的路由一致性面试官你说按 user_id 取模路由那如果未来要扩容从 16 库到 32 库怎么保证老数据还能查到候选人这是个经典问题。我们采用“双写 数据迁移”策略。扩容期间新旧路由都写查询时先查新路由未命中再查旧路由。同时后台任务逐步迁移老数据完成后关闭旧路由。面试官那迁移过程中如何保证数据一致性比如用户正在下单候选人我们会在迁移期间锁定该用户的分片通过 Redis 分布式锁暂停写入迁移完成后再释放。或者采用“增量同步 最终校验”允许短暂不一致后续对账补偿。面试官你们有没有考虑过用基因法或 range 分片候选人基因法适合有业务维度的场景比如 tenant_id 嵌入 user_id。但我们当前业务 user_id 是全局唯一无法提取基因。range 分片适合时间序列但用户分布不均容易热点。所以我们还是选了哈希取模。面试点评本场面试主要考察候选人在 JVM 对象生命周期、并发容器设计、缓存架构及分库分表等核心模块的深度理解。重点卡点在于是否能讲清楚技术选型背后的取舍如 size() 的近似性是否具备线上问题应对经验如哈希倾斜、扩容迁移是否能将原理与项目落地结合如布隆过滤器动态扩容。候选人需避免只背八股文要体现“为什么这么做”和“不这么做的风险”。技术补丁包对象逃逸分析原理JVM 判断对象是否逃逸出方法作用域未逃逸则可能栈上分配或标量替换。 设计动机减少堆内存分配降低 GC 压力。 边界条件对象被返回、赋值给静态变量或传入其他线程即视为逃逸。 落地建议避免在热点循环中创建临时对象依赖 JVM 自动优化。TLABThread-Local Allocation Buffer原理每个线程在 Eden 区拥有私有分配缓冲区避免全局锁竞争。 设计动机提升高并发下对象分配性能。 边界条件对象过大或 TLAB 用满时进入慢速路径。 落地建议可通过-XX:UseTLAB开启默认开启监控TLAB分配失败率。ConcurrentHashMap 1.8 实现原理CAS synchronized 锁桶头节点链表转红黑树优化查询。 设计动机提升并发度避免分段锁的固定并发限制。 边界条件哈希冲突严重时锁竞争加剧可能退化为重量级锁。 落地建议合理设置初始容量避免频繁扩容。ConcurrentHashMap size() 实现原理基于 LongAdder 机制baseCount CounterCell 数组统计。 设计动机无锁化统计提升并发性能。 边界条件返回近似值非实时精确值。 落地建议不用于业务逻辑判断仅用于监控或日志。布隆过滤器误判率控制原理通过位数组大小 m 和哈希函数数量 k 控制误判率。 设计动机以空间换时间快速判断 key 是否存在。 边界条件无法删除元素扩容复杂。 落地建议使用 RedisBloom 模块或 Guava 实现预估数据量设置参数。布隆过滤器动态扩容原理双层过滤器策略旧过滤器只读新过滤器写入。 设计动机支持数据增长避免重建。 边界条件查询性能下降需维护两层状态。 落地建议结合 TTL 自动淘汰旧过滤器。分库分表哈希路由原理对 user_id 哈希后取模定位分片。 设计动机数据均匀分布查询效率高。 边界条件扩容需数据迁移哈希倾斜风险。 落地建议使用一致性哈希或虚拟节点缓解倾斜。分库分表扩容策略原理双写 数据迁移 查询回源。 设计动机保证扩容期间服务可用。 边界条件迁移期间短暂不一致需对账补偿。 落地建议结合分布式锁或消息队列保证一致性。大对象直接晋升老年代原理超过 PretenureSizeThreshold 的对象直接在老年代分配。 设计动机避免年轻代频繁 GC。 边界条件可能导致老年代碎片化。 落地建议合理设置阈值监控老年代使用率。G1 Humongous Region原理大于 Region 50% 的对象分配至 Humongous Region。 设计动机高效处理大对象避免复制开销。 边界条件Humongous 对象回收依赖 Full GC。 落地建议避免频繁创建超大对象调整 Region 大小。锁升级过程原理偏向锁 → 轻量级锁 → 重量级锁由竞争激烈程度触发。 设计动机适应不同并发场景平衡性能与开销。 边界条件偏向锁在竞争激烈时失效。 落地建议高并发场景可禁用偏向锁-XX:-UseBiasedLocking。缓存穿透防护原理布隆过滤器 空值缓存。 设计动机防止恶意查询击穿数据库。 边界条件布隆过滤器有误判空值缓存占用内存。 落地建议空值设置短过期时间定期清理。跨分片查询解决方案原理通过 ES 构建二级索引。 设计动机支持复杂查询避免全表扫描。 边界条件数据同步延迟索引维护成本。 落地建议使用 Canal Kafka 实现近实时同步。哈希函数选择原理MurmurHash、CityHash 等算法分布更均匀。 设计动机避免 Java hashCode 的局部性偏差。 边界条件不同算法性能差异。 落地建议压测验证分布均匀性。分布式锁在分片迁移中的应用原理通过 Redis 或 Zookeeper 实现互斥访问。 设计动机保证迁移期间数据一致性。 边界条件锁超时、脑裂风险。 落地建议设置合理超时时间配合看门狗续期。