去年我在帮一个朋友做面试 mock,他已经背了三遍八股文,把 ThreadLocal 的原理、G1 的 Region 划分、MySQL MVCC 的版本链说得滚瓜烂熟。结果字节一面还是挂了。
面试官的反馈只有一句话:「基础知识点背得挺熟,但说不清楚为什么这样设计,也没有自己的判断。」
这就是字节一面和其他公司一面的本质差别。字节一面不是考你「背了多少」,考的是你「有没有想过为什么」。
这 50 道题是我整理了 2025-2026 年字节后端一面的真实面经,按六个知识域分类。每道题除了给答题要点,还会标注「考官想听到什么角度」——把问题放进这个框架里回答,命中率远高于背书式作答。

大多数公司并发考到「synchronized 和 ReentrantLock 的区别」就结束了。字节会追问锁升级的具体条件、AQS 的等待队列实现、以及 ThreadLocal 在线程池场景下的内存泄漏是怎么发生的。
这个模块出现在超过 80% 的字节 Java 一面中,丢分也最多。

Q1. synchronized 和 ReentrantLock 的核心区别是什么?
考官想听的角度:别只列功能差异,说出「什么场景下只能用 ReentrantLock」。
答题要点:
tryLock(timeout) 超时等待、lockInterruptibly() 响应中断,以及多个 Condition 对象(替代 Object 的 wait/notify 组,精确唤醒特定线程)加分项:「JDK 15 之后偏向锁被默认关闭,因为维护成本高且现代程序竞争普遍,这时两者的性能差距进一步缩小。」
Q2. synchronized 的锁升级过程是怎样的?
考官想听的角度:说出偏向锁到重量级锁的条件,不是只说「三种状态」。
答题要点:
Q3. volatile 的作用,以及它为什么不能保证原子性?
考官想听的角度:把可见性和有序性分开说,并给出一个 i++ 不安全的具体例子。
答题要点:
i++ 实际是三条字节码指令(getstatic、iadd、putstatic),volatile 只能保证每条指令读写的可见性,但三条指令之间仍可能被其他线程插入volatile boolean running)、双重检查锁中防止半初始化对象的指令重排Q4. ThreadLocal 内存泄漏是怎么发生的?怎么避免?
考官想听的角度:这是字节的高频追问题,必须说到 WeakReference 和线程池的关系。
答题要点:
ThreadLocalMap 的 key 是 WeakReference<ThreadLocal>,value 是强引用threadLocal.remove(),放在 finally 块加分项:「Spring 框架的 TransactionSynchronizationManager 用的就是 ThreadLocal,它在 cleanupTransactionInfo() 里有 remove,这是规范做法。」
Q5. AQS 的核心实现原理是什么?
考官想听的角度:说出 state 变量 + CLH 等待队列 + tryAcquire 模板方法的三角关系。
答题要点:
volatile int state(表示锁/资源状态)、当前持锁线程、FIFO 等待队列(CLH 变体)LockSupport.park 挂起LockSupport.unpark)ReentrantLock、Semaphore、CountDownLatch 都是 AQS 的子类,只需实现 tryAcquire/tryRelease(独占)或 tryAcquireShared/tryReleaseShared(共享)Q6. ConcurrentHashMap 的线程安全是怎么实现的?JDK 7 和 JDK 8 的差异?
答题要点:
Node[] table 代替 Segment,锁粒度细化到每个桶的头节点(synchronized + CAS)。空桶写入用 CAS,发生碰撞才加 synchronizedsize() 方法用 CounterCell 分散计数,避免计数器本身成为竞争瓶颈Q7. 线程池的核心参数,以及任务提交的完整流程?
考官想听的角度:必须说出拒绝策略触发的具体条件,很多人在这里答错。
答题要点:
corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handlerAbortPolicy(抛异常,默认)、CallerRunsPolicy(调用者线程执行)、DiscardPolicy(静默丢弃)、DiscardOldestPolicy(丢队头最老的任务)加分项:「生产里要用 CallerRunsPolicy 做反压,避免任务被静默丢掉。」
Q8. happens-before 原则是什么?
答题要点:
Q9. 什么是伪共享(False Sharing)?如何解决?
答题要点:
@Contended 注解(需 -XX:-RestrictContended)自动填充,LongAdder、ConcurrentHashMap.CounterCell 都用了这个技术Q10. CompletableFuture 和 Future 的区别?
答题要点:
Future.get() 是阻塞调用,没法链式组合多个异步任务CompletableFuture 实现了 CompletionStage,支持 thenApply(同步转换)、thenCompose(异步链式)、thenCombine(合并两个结果)、exceptionally(异常处理)ForkJoinPool.commonPool() 执行,也可以传入自定义线程池(推荐,避免公共池被占满)CountDownLatch + Future.get() 的组合JVM 模块字节通常不问「堆结构是什么」这种入门题,而是问「你们线上 Full GC 怎么排查」「G1 什么时候会退化为 Full GC」「类加载什么情况下会破坏双亲委派」。
Q11. G1 GC 的工作原理,以及它相比 CMS 的优势?
考官想听的角度:说出 Region 设计的核心动机——可预测的停顿时间。
答题要点:
加分项:「Region 内部对象跨 Region 引用靠 Remembered Set 维护,这是 G1 写屏障的代价,内存占用通常比 CMS 多 10-20%。」
Q12. Minor GC、Major GC、Full GC 的触发条件分别是什么?
答题要点:
System.gc();老年代空间不足;方法区空间不足;Minor GC 后晋升到老年代的对象超过老年代剩余空间(晋升担保失败)Q13. 类加载的双亲委派机制,哪些情况下会破坏它?
考官想听的角度:给出真实场景,不是背定义。
答题要点:
DriverManager 在 Bootstrap ClassLoader 里,但需要加载各厂商的 Driver 实现(在应用 classpath),因此用了线程上下文类加载器(Thread.getContextClassLoader())反向委派Q14. JVM 的内存区域划分,哪些区域可能 OOM?
答题要点:
Q15. 如何定位一次生产 Full GC 频繁的问题?
考官想听的角度:说出排查工具链,而不只是说「看 GC 日志」。
答题要点:
-Xlog:gc*:file=gc.log:time(JDK 9+),观察 Full GC 频率和每次停顿时间jmap -histo:live <pid> 看存活对象分布,找排名靠前的大对象jmap -dump:format=b,file=heap.hprof <pid> dump 堆,MAT 分析 Retention Heap,找引用链Q16. ZGC 的核心特点是什么?适合什么场景?
答题要点:
Q17. 什么情况下对象会直接进入老年代?
答题要点:
-XX:PretenureSizeThreshold,默认关闭,设置阈值后超过该大小直接进老年代)-XX:MaxTenuringThreshold)Q18. 什么是 TLAB?为什么需要它?
答题要点:
字节 MySQL 模块通常先问索引,再追问事务和锁,最后看情况聊 binlog/redo log。最近两年出现了「分库分表的跨库查询怎么做」的场景题,需要有具体方案。
Q19. MySQL 为什么用 B+ 树做索引,而不是 B 树或哈希表?
考官想听的角度:从磁盘 I/O 的视角解释,而不只是说「B+ 树叶子节点有链表」。
答题要点:
BETWEEN、ORDER BY),B 树做不到Q20. 聚簇索引和非聚簇索引的区别?回表是什么?
答题要点:
EXPLAIN 中 Extra: Using index 表明覆盖索引命中Q21. 最左匹配原则,什么情况下联合索引会失效?
答题要点:
(a, b, c) 实际按 a → b → c 排序。查询必须从最左列开始,且不能跳列WHERE b=1,不用 a);最左列使用范围查询后,后续列不走索引(WHERE a > 1 AND b = 2,b 不走索引);对索引列使用函数(WHERE YEAR(create_time) = 2024);隐式类型转换(字符串列用数字匹配);LIKE '%前缀'(通配符在开头)Q22. EXPLAIN 执行计划怎么看?重点关注哪些字段?
答题要点:
type:访问类型,性能从好到差:system > const > eq_ref > ref > range > index > ALL。ALL 就是全表扫描,需要优化key:实际使用的索引,NULL 表示未命中索引rows:估算扫描行数,越小越好Extra:Using index(覆盖索引)、Using where(需要再过滤)、Using filesort(文件排序,可能需要加索引)、Using temporary(用了临时表,需要优化)possible_keys 和 key 不一致时,说明优化器选了另一个索引或放弃索引Q23. MySQL 的事务隔离级别,以及 InnoDB 默认使用哪种?
答题要点:
Q24. InnoDB 的行锁类型有哪些?间隙锁的作用?
答题要点:
SHOW ENGINE INNODB STATUS 查看最近一次死锁信息Q25. redo log 和 binlog 的区别?两阶段提交是怎么回事?
考官想听的角度:说出「为什么需要两阶段提交」,而不只是描述格式差异。
答题要点:
Q26. 什么是慢查询?如何定位和优化?
答题要点:
slow_query_log=ON,long_query_time=1(默认 1 秒),记录超时查询pt-query-digest 按执行频率 × 耗时排序,找出最值得优化的 SQLSELECT *、避免函数计算索引列)→ 分页优化(大 offset 用 WHERE id > last_id)→ 分表(数据量超过 5000 万考虑)Q27. MVCC 的 Read View 和版本链是怎么工作的?
答题要点:
trx_id(最后修改该行的事务 ID)和 roll_pointer(指向 undo log 的版本链)m_ids(当前活跃事务列表)、min_trx_id、max_trx_id、creator_trx_id字节 Redis 模块的特点是不只考「说一下缓存穿透」,会追问「你们用的哪种布隆过滤器实现、误判率是多少」这类需要实际操作经验的问题。
Q28. Redis 为什么快?单线程不是应该慢吗?
考官想听的角度:这是字节高频问法,有反直觉的味道,要说出「单线程避免了什么」。
答题要点:
Q29. 缓存穿透、缓存击穿、缓存雪崩,分别怎么解决?
答题要点:
缓存穿透(查询不存在的数据,每次都打到 DB):
缓存击穿(热 key 过期,大量请求同时穿透到 DB):
缓存雪崩(大量 key 同时过期或 Redis 宕机,请求全打到 DB):
Q30. Redis 持久化:RDB 和 AOF 怎么选?
考官想听的角度:说出各自的数据丢失量和性能代价,而不是只列格式差异。
答题要点:
appendfsync always(每次刷盘,最安全,性能最差)、everysec(每秒刷,最多丢 1 秒)、no(OS 决定,性能最好但不可控)everysec + 每天 RDB 备份,平衡数据安全和恢复速度Q31. Redis 的数据类型及其底层实现?
答题要点:
Q32. Redis 的过期删除策略和内存淘汰策略?
答题要点:
allkeys-lru(全部 key 中 LRU 淘汰,缓存场景推荐)、volatile-lru(只淘汰设了 TTL 的 key)、allkeys-random、noeviction(拒绝写)allkeys-lru,避免没设 TTL 的 key 永不淘汰撑满内存Q33. Redis 分布式锁的实现,以及 Redlock 的问题?
答题要点:
SET key value NX PX 30000(原子操作,NX 不存在才设,PX 设毫秒过期)字节网络模块不考「OSI 七层模型是什么」,考的是「TIME_WAIT 为什么等 2MSL」「epoll 和 select 的性能差距从哪来」这类需要理解底层的问题。
Q34. TCP 三次握手的过程,为什么不能是两次?
考官想听的角度:说出「两次握手的具体问题是什么」,而不只是复述流程。
答题要点:
Q35. TIME_WAIT 状态为什么等 2MSL?大量 TIME_WAIT 怎么处理?
答题要点:
SO_REUSEADDR + tcp_tw_reuse(允许 TIME_WAIT 端口用于新连接,需配合时间戳 PAWS);启用 HTTP Keep-Alive 减少连接创建/销毁次数Q36. epoll 相比 select/poll 的优势在哪里?
考官想听的角度:从「每次调用的开销」和「事件通知机制」两个维度说,而不只说「epoll 没有数量限制」。
答题要点:
epoll_ctl),内核用红黑树维护监听 fd,事件发生时通过回调加入就绪链表(epoll_wait 只返回就绪的 fd),O(1) 复杂度Q37. TCP 如何保证可靠传输?
答题要点:
Q38. HTTP/1.1、HTTP/2、HTTP/3 的主要差异?
答题要点:
Q39. 进程和线程的区别,什么是协程?
答题要点:
Q40. 什么是用户态和内核态?系统调用的开销在哪里?
答题要点:
writev 一次写多块)、零拷贝(sendfile 避免用户态拷贝)、mmap 映射内存字节一面几乎 100% 会有算法题,通常 1-2 道。难度在 LeetCode Medium 左右,但要求代码不出错,有时现场 debug。以下是出现频率最高的题型,考的不只是解法,还有「你在写之前有没有说思路」。
Q41. 手撕 LRU Cache
字节出现频率最高的算法题,要求 O(1) 的 get 和 put。
核心思路:HashMap(key → 双向链表节点)+ 双向链表(维护访问顺序)
class LRUCache {
privatefinalint capacity;
privatefinal Map<Integer, Node> map;
privatefinal Node head, tail; // 哨兵节点
class Node {
int key, val;
Node prev, next;
Node(int k, int v) { key = k; val = v; }
}
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>();
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.prev = head;
}
public int get(int key) {
if (!map.containsKey(key)) return -1;
Node node = map.get(key);
moveToHead(node);
return node.val;
}
public void put(int key, int value) {
if (map.containsKey(key)) {
Node node = map.get(key);
node.val = value;
moveToHead(node);
} else {
Node node = new Node(key, value);
map.put(key, node);
addToHead(node);
if (map.size() > capacity) {
Node tail = removeTail();
map.remove(tail.key);
}
}
}
private void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
private void removeNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void addToHead(Node node) {
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
private Node removeTail() {
Node node = tail.prev;
removeNode(node);
return node;
}
}
Q42. 最长无重复子串(滑动窗口)
字节真实出现,要求 O(n) 时间复杂度。
核心思路:双指针(left, right)+ HashMap 记录字符的最新位置。right 遍历字符串,若当前字符在窗口内已出现,left 跳到该字符上次位置的下一位。
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> map = new HashMap<>();
int left = 0, maxLen = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (map.containsKey(c) && map.get(c) >= left) {
left = map.get(c) + 1; // 收缩左边界
}
map.put(c, right);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
Q43. 反转链表(以及区间反转 [m, n])
考官版本通常是「反转区间 [m, n] 的链表」,而不是简单的全链表反转。
核心思路:用 dummy 节点,找到第 m-1 个节点,然后在区间内用头插法逐个移动节点到区间起始位置。
Q44. 二叉树的层序遍历
BFS + 队列,每层单独收集。这道题在字节一面里会进一步追问:「之字形遍历」「只打印叶子节点」。
Q45. 合并两个有序链表
递归或迭代均可。递归写法更优雅但面试时要注意栈深度问题:
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
if (l1.val <= l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
Q46. 找到数组中第 k 大的元素(QuickSelect)
TopK 问题,字节喜欢考。O(n) 期望时间的 QuickSelect,而不是 O(n log n) 的排序。
核心思路:类似快排的分区,但只递归「k 所在的那半边」:
public int findKthLargest(int[] nums, int k) {
return quickSelect(nums, 0, nums.length - 1, nums.length - k);
}
private int quickSelect(int[] nums, int left, int right, int target) {
int pivot = partition(nums, left, right);
if (pivot == target) return nums[pivot];
return pivot < target
? quickSelect(nums, pivot + 1, right, target)
: quickSelect(nums, left, pivot - 1, target);
}
private int partition(int[] nums, int left, int right) {
int pivot = nums[right], i = left;
for (int j = left; j < right; j++) {
if (nums[j] <= pivot) { int t = nums[i]; nums[i++] = nums[j]; nums[j] = t; }
}
int t = nums[i]; nums[i] = nums[right]; nums[right] = t;
return i;
}
Q47. 最大子数组和(Kadane 算法)
经典动态规划,O(n) 时间。dp[i] = 以 nums[i] 结尾的最大子数组和 = max(nums[i], dp[i-1] + nums[i]),空间可以压缩到 O(1)。
Q48. 有效的括号(栈)
栈应用,遇到左括号压栈,遇到右括号判断栈顶是否匹配。字节会追问「设计一个支持 push、pop、min 的 O(1) min 栈」。
Q49. 二分查找变体:找旋转排序数组的最小值
旋转数组系列是字节高频题。核心是 mid 和 right 比较:如果 nums[mid] > nums[right],最小值在右半段;否则在左半段(含 mid)。
Q50. 动态规划:最少完全平方数(BFS 或 DP 均可)
字节一面真实出现(LeetCode 279)。DP 思路:dp[i] = min(dp[i - j*j] + 1),j 从 1 枚举到 j*j <= i。
Q:字节一面算法几道题,什么难度?
一般 1-2 道,LeetCode Medium 为主,少部分 Hard(二叉树系列、动规)。但字节面试官喜欢在你写完后追加约束或变形,「如果输入有重复元素呢」「如果要求 O(1) 空间呢」,这才是真正筛人的部分。写完就满足是不够的,要主动说出边界情况和复杂度分析。
Q:一面多长时间,知识点问多少?
通常 45-60 分钟。结构通常是:5 分钟自我介绍 + 项目深挖 → 20 分钟知识点(选 2-3 个领域,每个领域 3-4 道追问) → 20 分钟算法 → 5 分钟反问。知识点不是一道道顺序过,而是选几个点深挖到底。
Q:项目经历和知识点哪个更重要?
都重要,但项目是「差异化」的地方。知识点回答差不多,面试官会在项目上找区分度——「你说你用了分布式锁,具体是什么场景,为什么不用 DB 乐观锁」。没有实际项目里用过的技术,背再多八股也很容易被追问露馅。
Q:答题时发现某个原理没背到怎么办?
实话实说「这个细节我不太确定,我的理解是 XX,但可能有偏差」,然后说出你能推导出来的部分。比「编一个不确定的答案」好得多——字节面试官追几句就能判断你是在背书还是真正理解。
Q:Spring Boot 相关知识面吗?
一面通常不主动考 Spring,但如果你简历上写了 Spring 项目,会借此追问「Bean 的生命周期」「循环依赖怎么解决」「Spring 事务失效的场景」。所以写进简历的框架要确保能说清楚原理。
字节一面的通过率不高,不是因为题目超纲,而是因为大多数人准备的是「背答案」而不是「理解设计」。这 50 道题的答题要点都在往「为什么这样设计」的方向引,看到一道题脑子里先想「这个设计在解决什么问题」,而不是直接找对应的记忆片段。
写这篇的时候码哥翻了不少 2025-2026 的面经,发现字节这两年越来越爱考 Java 21 的特性(虚拟线程、Records、Pattern Matching)和 G1/ZGC 的对比。
下篇打算把字节二面的系统设计题整理一遍,二面和一面的考察维度差别挺大,关注一下,不然等用到了才来找就来不及了。身边有人在准备字节面试,这篇可以直接发给他,省他再去各处找面经的时间。