面灵AI→

网易云音乐全栈秋招一面:事件循环、排序手撕与 RAG 追问

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 请做一下自我介绍。
  2. 简单介绍一下你的项目。
  3. 什么是变量提升?
  4. 作用域链是什么?
  5. 垃圾回收机制是怎么工作的?
  6. 追问内存回收的实现:堆栈内存分配、新生代与老生代分别怎么回收?
  7. 事件循环是什么?
  8. 宏任务、微任务各有哪些?
  9. 追问:同步队列和异步队列(微任务、宏任务)是不是一回事?本质上有区别吗?
  10. 手写排序:快速排序、冒泡排序、插入排序、归并排序、选择排序、桶排序。
  11. 数据结构相关问题。
  12. 说说你对接口模型的理解,追问 controller、service 层。
  13. Redis 缓存与高并发方面有哪些理解?
  14. 重新介绍项目,选一个详细讲讲(要求按 STAR 法则)。
  15. 项目的难点是什么?有没有数据支持你的数据库读写性能优化了多少?
  16. 介绍一下 RAG。
  17. 追问 Agentic RAG、Hybrid RAG、C-RAG 这些名词分别是什么?
  18. SDD 是什么?你是怎么用 SDD 的?
  19. 你写过 Skill 吗?
  20. 反问环节。

《参考解析》

事件循环:把「同步队列」和「宏任务队列」分清

被追问「同步队列和异步队列是不是一回事、本质有区别吗」,这题答不准很容易乱。本质区别在于:同步代码执行在调用栈上,是「当前正在做的事」;宏任务与微任务队列是任务队列,是「排队等待被调度的事」。所谓同步队列并不是队列,而是调用栈加上当前这个宏任务的执行体。浏览器里一轮事件循环的次序是:取出一个宏任务执行,执行期间产生的微任务在本次宏任务结束后、下一个宏任务开始前全部清空(包括微任务里新产生的微任务),需要时执行渲染,然后进入下一轮。宏任务包括整段 script、setTimeout/setInterval、I/O、UI 事件、postMessage;微任务包括 Promise 的 then/catch/finally、queueMicrotask、MutationObserver,Node 里还有 process.nextTick,且 nextTick 队列优先于 Promise 微任务。Node 侧的差别要提一句:它有 timers、poll、check 等阶段划分,setImmediate 与 setTimeout 的次序也另有讲究,点出「同一套概念、不同实现」比背输出题更值钱。

垃圾回收:从引用计数讲到 V8 的分代回收

面试官问垃圾回收,通常期望你顺着「怎么判断该回收」→「怎么回收」→「分代怎么优化」三层讲。判断可达性用标记清除:从根(全局对象、调用栈上的引用、闭包环境)出发标记可达对象,不可达的回收;引用计数解决不了循环引用,所以 V8 只把它当辅助手段。回收算法上按对象存活时间分代:新生代对象存活短、空间小,用 Scavenge 把空间分成 from 与 to 两块,存活对象复制到 to 区(Cheney 算法,广度优先复制),多次存活后晋升到老生代;老生代空间大、存活多,用标记清除配合标记整理来处理碎片,为了不长时间阻塞主线程做成增量标记与并发标记。被反馈「要讲到堆栈、内存分配、新生代老生代才算满意」,说明这种题必须主动覆盖全部层次:栈上放的是原始值与引用,堆上是对象实体;新对象在新生代用 bump pointer 连续分配,大对象直接进老生代。最后补一句实践:浏览器里的内存泄漏多半来自未清理的定时器、事件监听、闭包长期持有大对象和 detached DOM,用堆快照对比来找。

排序手撕:六种一起考,考的是边界与稳定性

一次点名六种排序,说明面试官在看基础是否成体系。先给一张能随时说出的对照:冒泡、插入、选择平均 O(n²),其中插入在近乎有序时接近 O(n);快排平均 O(n log n)、最坏 O(n²)、不稳定、原地;归并稳定、O(n log n)、需要 O(n) 额外空间;桶排序在数据分布均匀且范围可控时能到线性,代价是额外空间,分布不均会退化。写的时候三处细节最容易失分:快排的 partition 用双指针还是挖坑、递归终止与基准选择(随机或三数取中,避免有序数组退化)、归并的临时数组不要每次递归新建。桶排序要能说清适用场景——数据量大但取值范围小,比如分数、年龄段,否则桶内退化成 O(n²)。手写时先写主干再补边界(空数组、单元素、含重复元素),写完主动跑一个例子说明,比默写速度更重要。

接口分层、Redis 与高并发:往架构与取舍上答

「接口模型的理解,追问 controller、service 层」看着虚,其实在问分层职责:controller 只做参数校验、鉴权上下文与响应包装,不写业务;service 承载业务规则与事务边界,一次业务操作对应一个方法;dao 或 mapper 只管数据访问,不掺业务判断。判据是「换掉表现层还需不需要改这段逻辑」——需要,它就不该在 controller。Redis 缓存与高并发部分要覆盖几组:缓存模式(旁路缓存最常用,读先查缓存再回源并回填,写先更新库再删缓存)、三大问题(穿透用空值缓存或布隆过滤器,击穿用互斥重建或逻辑过期,雪崩给过期时间加随机并做多级缓存)、热点 key 的处理(本地缓存、key 打散)、以及分布式下的锁与原子操作(SETNX 加过期、Lua 脚本、Redlock 的争议)。最后要敢说边界:缓存不是一致性方案,强一致场景要么不缓存,要么接受读旧值并做补偿。

项目难点与 RAG 家族名词:怎么把追问接住

「按 STAR 讲被打断、只要功能」和「难点不难,有数据支持吗」这两个反馈说明面试官反感的是空泛叙述。项目讲述的正确姿势是先给一句定位和你的角色,然后直接进技术决策:为什么这么设计、备选方案是什么、为什么否掉,最后给可验证的结果——QPS、延迟、内存、慢查询数量、故障率,哪怕是自己压测出来的数据,也远好过「优化了性能」。被追「有没有数据支持」时答不出就老实说明测量条件(没有独立环境,只能拿本地压测或慢日志前后对比),并说清如果重来会怎么埋点,比硬编一个数字安全。RAG 那串名词要能分清侧重:RAG 是「检索增强生成」的统称,把外部知识检索进上下文再让模型作答;Hybrid RAG 指检索侧做混合召回(向量加关键词或 BM25)再融合排序;Agentic RAG 是在检索外面套一层决策——由模型自己决定要不要检索、检索几轮、换不换查询,甚至调用工具校验;C-RAG 一般指带纠正或校验环节的 RAG,对检索结果的可信度做判断,不合格就重新检索或降级。名词记不牢时不要硬猜,可以先说「我理解它强调的是这一点,具体定义各家叫法可能不同」,再把 RAG 的完整链路(切分、嵌入、召回、重排、拼上下文、生成、引用)讲一遍——面试官抛名词多半是想看知识面,接住链路比背书更有效。

SDD 与 Skill:把流程变成可复用资产

这两问指向同一类能力:有没有一套把流程沉淀成资产的方法。SDD(Spec-Driven Development,规格驱动开发)的要点是先写规格再实现——把需求拆成可验收的条目,明确输入输出与约束,写清边界和失败情况,再由它驱动实现与评审,好处是变更可追溯、评审有依据、人和工具拿到同一份事实来源。Skill 则更像可加载的操作手册:把某类任务的方法、步骤、注意事项、示例组织成一组文件,按需加载,避免把所有上下文一次塞进窗口。答的时候落到自己的实践上——需求文档或接口契约怎么写的、团队里谁的脚本或提示词被沉淀成了共用资产、新人上手因此快了多少——比解释定义更打动人。这一场节奏偏紧、多次被打断,反问环节拿到了全栈发展建议;遇到打断不要慌,先把被打断那半句的结论说完再回答追问,节奏乱掉才是真正的扣分项。