面灵AI→

顺丰Java后端一面:线程池参数、JVM与GC、MySQL深分页优化

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

《面试题目》

  1. 你刚才说到 Java 里的并发,能描述一下在你自己的项目或实习实践里,是怎么通过配置线程池去解决并发的效率问题、或者资源竞争问题的?
  2. 你刚才说到设置的核心参数——核心线程数是 3 个,以及用了有界的队列,你当时是出于什么样的考虑设计了这么两个参数?
  3. 关于 GC 这一块,你用的什么垃圾回收器?
  4. 你能讲一下 G1,或者说它对比以前的 CMS,优缺点是什么吗?
  5. 在 JVM 的内存管理里面,哪些区域是线程私有?哪些区域是线程共有的?
  6. 现在假设一个服务器明确出现了内存溢出问题,你会怎么样去排查到底是什么原因导致的这个内存溢出?
  7. 针对数据库这一块,MySQL 你有做过一些慢查询的排查,或者索引优化的经验吗?
  8. 如果我们要查询一个比较大的页数,就是那种深分页的问题,你会怎么处理?
  9. 那你有没有看过优化之后前后性能提升了多少?有去看过这个数据吗?
  10. 在你这样的实现过程中,刚才说的是用 AI 去驱动,那你有没有遇到过:它可能做了自测也过了、AI 自己认为过了,但你实际看下来其实没有你想要的东西、或者达不到你想要的东西?有遇到过这些情况吗?
  11. 你能分享一次你自己在开发或者测试环境遇到某些棘手的 bug,然后你是怎么样去定位到这个 bug 的原因、最终是怎么解决的?
  12. 对于这样一个系统,我假设也有一个查询的功能,就是页面点一个按钮,能够从后台数据库返回数据。你能讲一下这一次请求,从前端点击按钮开始,到最终的数据返回前端,中间经过了哪些数据链路以及网络交互的过程?
  13. 你还有什么想问我的吗?(反问环节)

《参考解析》

线程池参数:面试官要的是取舍,不是参数表。 这题真正在问「你项目里为什么这么配」,回答要先给场景(批量调下游、异步落库、并发拉取),再说清为什么用池而不是随手 new Thread——线程创建销毁有开销,无上限的线程会把下游和自身都打挂。核心线程数 3 这种具体值,要能说出推导过程:CPU 密集型大致取核数(或核数 +1),I/O 密集型因为线程大部分时间在等待可以放大,经验公式是核数 ×(1+ 等待时间/计算时间),最终值靠压测校准;能讲出「我是按机器规格和压测结果定的」就比背公式可信。有界队列的理由必须点透:用无界队列(LinkedBlockingQueue 默认容量接近无限)时队列永远不满,线程数根本到不了 maximumPoolSize,任务只会无限堆积,表现为延迟飙升甚至 OOM;换成有界队列才能把过载暴露出来,再由拒绝策略决定是快速失败还是让调用方自己跑(CallerRunsPolicy),这就是背压。顺带可以说明执行顺序是「核心线程 → 队列 → 扩到最大线程数 → 拒绝」,很多人误以为是先扩容再排队;再补一句线程命名和队列长度监控,才算真上过生产。

GC 与 JVM 内存:答选型理由,别只报名字。 被问「用的什么回收器」时,只说 G1 或 CMS 太单薄,要连着场景答:JDK 8 默认是 Parallel(吞吐优先),JDK 9 起默认 G1;堆大、要求停顿可预期就选 G1,追求吞吐就 Parallel,极低延迟场景才谈 ZGC。G1 与 CMS 的对比要落到机制上:CMS 是老年代的并发标记清除,用标记-清除所以会产生碎片,碎片扛不住时退化成一次带压缩的 Full GC,停顿不可控,而且在 JDK 9 被弃用、14 移除;G1 把堆切成等大 Region,优先回收收益最高的那些(Garbage-First),可以设 MaxGCPauseMillis 做停顿预测,整体用复制算法所以没有碎片问题。代价也要说:G1 的写屏障(SATB)有额外 CPU 与内存开销,几 G 的小堆上并不比 Parallel 划算。内存区域这题要答准:线程私有是程序计数器、虚拟机栈、本地方法栈;线程共享是堆和方法区(JDK 8 起是元空间,改用本地内存),字符串常量池在 JDK 7 之后已挪到堆里。OOM 排查先分清种类——Java heap space、Metaspace、GC overhead limit exceeded、unable to create new native thread、Direct buffer memory 根因完全不同,不能一律当内存泄漏;heap 溢出的标准动作是启动参数加 -XX:+HeapDumpOnOutOfMemoryError,事后用 MAT 看 dominator tree 和 GC Roots 引用链找持有者,线上先限流/重启止血再留现场。判断是泄漏还是单纯不够用,看 Full GC 之后老年代能不能回到低位:回不去才是泄漏;常见根因是静态集合只增不删、ThreadLocal 没 remove、连接或流没关、一次查全表。

MySQL:慢查询定位、索引取舍与深分页。 慢查询的排查顺序是先用慢查询日志或监控把 SQL 捞出来,再 EXPLAIN 看 type、key、rows、filtered 和 Extra(出现 Using filesort、Using temporary 就要警惕),必要时 EXPLAIN ANALYZE 看真实耗时。走不上索引的常见原因要能数出来:联合索引不满足最左前缀、索引列被函数包住、隐式类型转换、区分度太低的列单独建索引(比如状态、性别)。索引不是越多越好,每个索引都会拖慢写入,所以要在查询收益和写放大之间取舍。深分页的优化有三条路,各自有边界:一是延迟关联,先在索引上只取主键再回表 join,减少回表次数;二是键集分页(游标),记住上一页最后一条的排序键写成 WHERE id > ? ORDER BY id LIMIT 20,翻页成本恒定,代价是只能「下一页」、不能跳页;三是业务侧限制可翻页深度或改用检索。注意 ORDER BY 的列和排序方向要和索引一致,否则每页都要 filesort。第 9 题问「优化前后提升多少」是在探你有没有量化习惯——准备时就把 P95 耗时、扫描行数、QPS、CPU 占用的前后数字记下来,并说清怎么测的;没测过就坦白没测,比编一个数字安全得多。

AI 辅助开发、排障方法与一次请求的全链路。 「AI 自测过了但结果不对」是 AI 编码最典型的失败模式:模型顺着自己的实现去写测试,只覆盖它想到的路径,等于自己给自己判卷。对策是把校验权收回来——测试从需求和接口契约出发写(先写用例再让模型实现),关键路径必须有能失败的断言,类型检查、lint、CI 当客观门,跑真实端到端而不只看单测,最后的 diff 由人 review。这题一定要举一个具体例子,落点放在「我改了流程」,而不是「模型不行」。排 bug 那题考的是方法论而非故事,按固定结构讲最稳:现象与影响面 → 先止损 → 缩小范围(看最近变更、二分、加日志而不是猜)→ 定位到可复现、可证伪的根因 → 修复 + 回归 + 补监控防复发。请求全链路那题按层次说清楚即可:前端组装请求并带上鉴权 → DNS 解析 → TCP 握手(HTTPS 再叠 TLS)→ CDN/网关/Nginx 做 TLS 卸载与负载均衡 → 后端接入层鉴权限流 → 业务逻辑 → 数据层从连接池取连接、SQL 进 MySQL 经过解析与优化器选索引、Buffer Pool 未命中才落磁盘 → 结果集序列化返回 → 浏览器渲染;能补一句「哪一段慢怎么定位」(浏览器 Network 的 TTFB、网关与应用耗时打点、慢查询日志)就比背流程高一层。最后复盘一下这场面试:面试官迟到、只聊 20-30 分钟、全程十二个技术追问而无手撕,候选人怀疑是 KPI 面;不过第 1、2、9、10 题都在追项目里的具体参数与数据,这类面试更像在核验简历的真实性。准备一面的性价比最高的做法,是把简历上每个技术点都练成「为什么这么选、量了多少、替代方案是什么」,反问环节提前备两三个关于团队和业务的问题。