资讯动态

Java高级后端 · 全套面试通关手册(线上故障排查)

发布时间:2026/10/9 5:34:05 来源:尧图企业网站定制
1. CPU飙高排查【Java 后端线上故障排查】面试核心一套完整排查链路从服务器定位 → 进程 → 线程 → 代码生产实战流程。一、整体排查步骤背诵主线流程先用 top 命令查看整机资源定位是哪个进程 CPU 高找到高 CPU 进程 PID查看该进程内部线程占用导出线程栈定位占用 CPU 的线程翻译成可读线程 ID查看栈信息定位代码结合业务判断代码问题修复二、分步命令(1)top查看服务器 CPU、内存。按P按 CPU 排序找到 CPU 高的 PID。(2)top -Hp PID查看该进程下所有线程按 P 排序找到高 CPU 线程 TID。拿到 TID 后转 16 进制printf %x\n TID(3)jstack PID打印 Java 进程线程堆栈找到 16 进制线程号定位代码。注意jstack 要和进程 JDK 版本保持一致高负载时 jstack 可能卡顿。备选jstat -gc PID 1000观察 GC 情况区分是代码业务逻辑 CPU还是GC 导致 CPU 飙升。三、两大类根因业务代码 / GC 问题类型 1业务代码导致 CPU 高线程持续跑典型场景死循环while (true) 无退出条件循环内没有 sleep疯狂占用 CPU。复杂计算、大量字符串拼接、正则匹配、大集合遍历。频繁序列化 / 反序列化大数据对象处理。频繁创建大量临时对象或者大量复杂排序。栈特征线程 RUNNABLE 状态卡在业务方法。类型 2GC 频繁导致 CPU 飙高非常高频考点现象jstat 看到 YGC 频繁FullGC 频繁。 原因内存分配不合理堆太小代码大量创建短生命周期对象快速填满新生代内存泄漏集合长期持有对象无法释放大对象直接进入老年代频繁触发 Full GC。栈特征多个 GC 工作线程占用大量 CPU。区分判断口诀看 jstat。GC 曲线持续上涨GC 次数暴涨 → GC 问题GC 正常业务线程 RUNNABLE 占 CPU → 代码死循环 / 复杂计算。四、补充工具arthas阿里诊断工具生产最常用# 查看进程CPU thread -n 5 # 查看GC状态 gc # 查看方法耗时 trace 包名.方法名arthas 更方便不用手动转 16 进制线程 ID线上优先用 Arthas。五、排查后解决方案死循环修复循环退出条件增加休眠。GC 频繁优化代码减少临时对象调整堆参数排查内存泄漏。复杂计算异步处理、缓存结果、减少重复计算。高频深挖面试追问Q1top 看到 CPU100%怎么区分是业务代码还是 GC答使用 jstat -gc 观察 GC 指标。如果 YGC 频繁、老年代持续上涨GC 线程占用高就是 GC 问题GC 次数正常业务应用线程 RUNNABLE 占用 CPU则是业务代码死循环或者密集计算。Q2jstack 拿到线程栈如果是 GC 线程在跑下一步怎么做答dump 堆jmap -dump:formatb,filexxx.hprof PID下载 hprof 文件用 MAT 分析查找内存泄漏、大对象。Q3jstack 有什么注意事项高负载下多次 jstack 对比单次栈可能只是瞬时状态不要频繁执行会暂停应用最好 2~3 次间隔采样看持续热点线程。Q4Arthas 中 thread -n 5 含义打印当前 CPU 最高的 5 个线程直接看到线程栈省去手动转十六进制线上首选。Q5CPU 高但是 GC 正常可能是什么原因死循环、大量循环遍历、复杂正则、大量编解码、密集计算。一句话背诵总结CPU 飙高排查流程top 找进程 → top -Hp 找高 CPU 线程 → TID 转 16 进制jstack 打印线程栈定位代码区分是业务死循环密集计算还是频繁 GCGC 问题 dump 堆用 MAT 分析内存泄漏生产推荐 Arthas 快速定位热点线程和方法。2. OOM 内存溢出OOMOutOfMemoryErrorJVM 没有足够内存分配对象抛出该错误OOM ≠ 内存泄漏内存泄漏是导致 OOM 最常见原因。一、常见 OOM 类型Java heap space堆 OOM最常见堆内存不足无法创建新对象。大多是内存泄漏或是一次性分配超大对象。Metaspace元空间 OOMJDK8元空间存放类信息、常量、方法。原因动态生成大量 ClassASM/Cglib 反射、动态代理、热部署类加载不回收。Unable to create new native thread无法创建新线程创建线程过多。每个线程占用栈内存线程数量耗尽系统资源。Direct buffer memory直接内存 OOMNIO 使用堆外内存手动分配却忘记释放堆外内存不受堆大小限制但受系统总内存限制。二、完整排查步骤背诵主线1.查看应用日志确认 OOM 异常类型区分是堆 / 元空间 / 直接内存 / 线程栈 OOM。2.JVM 启动参数增加-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/xxxOOM 发生自动 dump 堆快照 hprof 文件。3.拿到 hprof 文件使用 MATMemory Analyzer Tool分析。4.MAT 重点看Histogram 直方图查看对象实例数量、占用内存找出异常大对象Dominator Tree 支配树找出占据大量内存的对象看是谁持有引用Leak SuspectsMAT 自动给出疑似内存泄漏报告5.定位代码查看对象引用链判断对象为什么无法被 GC 回收。6.复现或查看业务逻辑修复代码。三、常见原因1. Java heap space 堆 OOM内存泄漏集合List/Map静态全局集合不断 add 元素没有清理对象一直持有引用无法 GC。一次性加载大量数据数据库查询select *一次性查出几十万数据放入内存集合。堆内存设置过小业务流量上涨正常对象就撑满堆。2. Metaspace 元空间 OOM频繁动态代理、CGLIB、ASM 动态生成类热部署框架重复加载 class旧类没有卸载参数-XX:MaxMetaspaceSize设置元空间上限3. Direct buffer memory 直接内存 OOMNIO、Netty 使用 ByteBuffer 分配堆外内存没有调用 release 释放。 堆外内存 GC 回收滞后容易 OOM。4. Unable to create new native thread创建大量线程线程栈占用操作系统内存。线程数量受操作系统最大进程数限制。四、排查命令 工具1.jmap -dump:formatb,filexxx.hprof PID手动 dump 堆尽量业务低峰执行会 STW2.jstat -gc PID 1000持续观察堆内存、GC 变化看老年代持续上涨3.Arthasheapdump直接 dump 堆快照memory查看内存使用情况4.MAT分析 hprof定位泄漏对象、引用链五、解决方案集合用完及时清理避免静态集合长期持有对象。数据库大数据查询分页、流式读取不要一次性加载全量数据到内存。合理设置 JVM 堆、元空间大小不要过大 / 过小。NIO/Netty 堆外内存使用完主动释放。控制线程数量使用线程池禁止手动 new Thread 无限制创建线程。高频深挖面试追问Q1OOM 和内存泄漏的区别内存泄漏对象不再使用但 GC 无法回收内存缓慢上涨最终耗尽内存引发 OOM。 OOM 是结果内存泄漏是最常见原因OOM 也可以是一次性申请超大对象不存在泄漏。Q2发生 OOM 之后进程会直接退出吗不一定。抛出 OOM 的线程终止其他线程继续运行。但内存已经不足后续随时再次 OOM。建议配置自动 dump 后重启。Q3为什么元空间会 OOM元空间存 Class 信息。动态代理、热部署会大量创建 class类只有在类加载器回收时才卸载。大量动态类导致元空间持续增长。Q4堆 dump 什么时候采集必须在 OOM 发生那一刻 dump。在 OOM 之后很久再手动 dump泄漏对象可能已经被回收看不到现场。所以 JVM 参数配置 HeapDumpOnOutOfMemoryErrorOOM 自动 dump。Q5MAT 支配树是干嘛的支配树看对象如果被回收能释放多少内存。快速定位占用内存最大的对象追踪引用链找到泄漏代码。一句话背诵总结OOM 分堆、元空间、直接内存、线程创建失败四类配置 JVM 参数 OOM 自动 dump 堆快照用 MAT 分析 hprof通过直方图、支配树、引用链定位堆 OOM 大多是静态集合内存泄漏或者一次性加载大量数据元空间 OOM 多是动态类 / 热部署堆外内存记得主动释放优先使用线程池控制线程数量。3. FullGC频繁前置回顾Full GC 会回收整个堆新生代 老年代会STWStop The World暂停所有业务线程频繁 FullGC 会造成接口卡顿、超时是线上重大问题。一、触发 FullGC 的常见原因背诵1.老年代空间不足对象晋升到老年代老年代剩余空间放不下触发 FullGC。短生命周期对象过多快速占满新生代大量对象快速晋升到老年代内存泄漏长生命周期集合持续持有对象老年代持续涨占满堆2.大对象直接进入老年代超过-XX:PretenureSizeThreshold阈值的对象绕过新生代直接进老年代大量大对象快速填满老年代。3.动态年龄判断新生代对象年龄达到阈值晋升到老年代动态年龄规则同年龄对象总大小超过 survivor 一半 该年龄全部晋升到老年代。4.元空间满JDK8 Metaspace元空间占用到达 MaxMetaspaceSize触发 FullGC尝试卸载类。5.System.gc()代码手动调用System.gc()建议禁用-XX:DisableExplicitGC。6.CMS特有CMS 并发失败、浮动垃圾触发 Serial Old 兜底 FullGCG1/ZGC 很少二、排查步骤主线流程面试口述1.用jstat -gc PID 1000持续观察GC 指标FGC 次数、FGCT 耗时看老年代 O 不断上涨每次 FGC 后下降很快又涨上来大概率内存泄漏YGC 频繁每次 YGC 后大量对象晋升到老年代2.查看 GC 日志定位 FullGC触发原因GC 日志加参数-Xloggc:xxx.log3.如果怀疑内存泄漏dump 堆jmap -dump:formatb,filexxx.hprof PIDMAT 分析找长存活对象、引用链4.观察大对象看是否一次性加载大批量数据查询全表放入 List5.检查代码有没有手动 System.gc 调用6.查看元空间占用判断是否类加载过多三、区分两种典型现象场景 1FullGC 后内存下降很快再次涨满内存泄漏特征每次 FullGC 能释放一部分内存但短时间老年代迅速被占满FGC 持续触发。根源对象不再使用但存在强引用无法回收。解决MAT 分析堆快照找到泄漏对象清理集合引用。场景 2FullGC 后内存大幅下降平稳一段时间非泄漏特征业务正常流量产生大量对象一次性大量对象晋升老年代。根源堆参数不合理、大对象、短生命周期对象太多。解决优化代码减少临时对象调整堆、晋升阈值。四、解决方案背诵1.代码层面优先避免一次性查询大量数据分页 / 流式读取不要一次性放入内存集合集合使用完毕及时清除引用静态集合谨慎使用减少循环内创建大量临时对象移除代码里System.gc()JVM 参数禁用显式 GC2.JVM 参数调优合理设置新生代大小不要新生代过小对象快速晋升到老年代合理设置PretenureSizeThreshold控制大对象晋升规则设置MaxMetaspaceSize防止元空间触发 FullGC3.架构层面大对象做缓存 / 异步处理不占用 JVM 堆内存内存泄漏无法快速修复可增加监控告警定时重启兜底五、高频深挖面试追问Q1FullGC 为什么 STW 时间很长FullGC 扫描整个堆堆越大STW 时间越长。生产堆不要设置过大比如几十 G 堆一次 FGC 停顿极长。Q2YGC 频繁会不会引发 FullGC会。YGC 频繁survivor 放不下大量对象晋升到老年代老年代持续填满触发 FullGC。Q3G1 还会有 FullGC 吗G1 目标减少 FullGC但依然会触发。比如混合收集跟不上内存增长、分配大对象失败G1 会退化为 Serial Old 的 FullGCSTW 非常久。ZGC 几乎不会 FullGC。Q4老年代每次 FullGC 之后内存只释放一点点是什么问题典型内存泄漏。大量对象是强引用GC 无法回收只能释放少量弱引用对象。老年代持续上涨FGC 越来越频繁。Q5如何区分是内存泄漏还是正常业务流量导致 FullGC看 GC 后老年代内存水位。FGC 之后内存明显回落属于业务正常对象FGC 之后内存下降很少很快又涨满就是内存泄漏需要 dump 堆分析。一句话背诵总结FullGC 回收整个堆产生长时间 STW常见诱因老年代占满、内存泄漏、大对象、元空间满、手动 System.gcjstat 观察 GC 指标GC 日志定位触发原因FGC 后内存回落少大概率内存泄漏dump 堆用 MAT 排查优先优化代码谨慎调 JVM 参数G1 依然会 FullGCZGC 大幅降低 FullGC 概率。4.死锁排查死锁定义多个线程互相持有对方所需要的锁并且都不释放形成循环等待所有线程永久阻塞业务卡住CPU 不一定高。死锁四大必要条件全部满足才会发生死锁。一、死锁四大必要条件必背互斥条件资源是独占锁同一时刻只能一个线程持有synchronized、ReentrantLock 排他锁。持有并等待线程已经持有锁在不释放已有锁的情况下继续去申请其他锁。不可剥夺已经拿到的锁不能被其他线程强行抢占只能持有者主动释放。循环等待线程之间形成闭环A 等 B 的锁B 等 A 的锁。破坏任意一条就可以消除死锁。工程上最常用统一锁获取顺序破坏循环等待。二、Java 死锁排查完整流程背诵主线现象接口请求卡住、超时线程一直阻塞CPU 不高。命令jps找到 Java 进程 PIDjstack PID打印线程栈。jstack 会自动检测死锁在输出末尾找到Found one Java-level deadlock。查看输出找到线程名称、锁信息、代码行号看两个线程各自持有的锁、等待的锁。定位业务代码分析获取锁顺序问题。修改代码调整锁顺序重启验证。Arthas 工具thread命令会直接识别死锁线程无需手动解析堆栈。三、典型死锁代码场景场景线程 1 先拿锁 A再申请锁 B线程 2 先拿锁 B再申请锁 A。//线程1 synchronized(A){ synchronized(B){ } } //线程2 synchronized(B){ synchronized(A){ } }互相持有对方需要的锁循环等待死锁。四、死锁与活锁、线程饥饿区分面试高频对比死锁线程阻塞不再运行互相等锁永久卡住。活锁线程没有阻塞一直重试但始终无法执行成功比如自旋锁互相谦让CPU 占用。线程饥饿线程一直拿不到资源长期得不到执行例如低优先级线程高优先级持续抢占资源。五、如何避免死锁背诵统一锁获取顺序最核心所有线程获取锁都按固定顺序先 A 后 B杜绝循环等待。控制锁粒度缩小锁范围减少持有锁的时间。不要在锁内部调用外部接口。使用带超时的锁tryLock(time,unit)获取锁超时主动放弃释放已拿到锁避免永久等待。避免嵌套锁尽量不写多层 synchronized 嵌套。代码评审阶段检查嵌套锁逻辑。高频深挖面试追问Q1jstack 检测到死锁进程会自动退出吗不会。发生死锁的线程卡住但进程不会 crash。业务请求阻塞超时服务还在运行需要人工重启修复代码。Q2synchronized 和 ReentrantLock 哪个更容易产生死锁二者都可能死锁。synchronized 获取锁不能设置超时ReentrantLock 的 tryLock 可以加超时能够主动规避死锁。Q3怎么区分死锁和普通锁等待普通锁等待一个锁被占用其他线程排队锁持有者执行完会释放等待线程后续能拿到锁。 死锁多个线程循环等待谁都无法释放锁永远无法解除jstack 会明确标记 Found deadlock。Q4tryLock 为什么可以防止死锁获取锁设置超时时间如果指定时间拿不到锁线程主动放弃并且释放自己已经获取的锁打破持有并等待条件。Q5数据库也会发生死锁和 Java 锁死锁一样吗原理相似都是循环等待。MySQL InnoDB 行锁死锁数据库引擎会自动检测选择代价小事务回滚Java 线程死锁 JVM 不会自动处理需要人工干预。一句话背诵总结死锁四大条件互斥、持有并等待、不可剥夺、循环等待用 jstack 检测输出会直接标记死锁典型场景嵌套锁获取顺序不一致优先统一锁顺序避免死锁tryLock 带超时可主动释放锁死锁线程阻塞进程不会自动退出区分死锁、活锁、线程饥饿。5. 接口偶发超时重点偶发最难排查不是必现。大概率不是代码逻辑 bug多是资源抢占、锁等待、GC、网络、中间件抖动等问题。一、排查思路背诵主线流程1.查看日志超时接口的调用链路日志、异常堆栈看卡在哪个环节DB、Redis、MQ、外部 HTTP 调用2.监控大盘看 CPU、内存、GC、线程数、连接池指标看超时时刻是否伴随指标异常3.链路追踪SkyWalking/Pinpoint查看 span 耗时定位是哪一段慢4.抓线程栈超时瞬间 jstack/arthas thread看业务线程阻塞在哪里5.区分是服务内部慢还是下游依赖响应慢、网络抖动二、常见根因分类1.JVM 层面GC 停顿非常高频FullGC / YGC 长时间 STW业务线程暂停接口超时。 特征超时时间点监控看到 GC 耗时突增。 排查jstat、GC 日志。2.锁等待synchronized / ReentrantLock部分线程拿到锁长时间不释放锁内慢查询、调用外部接口其他请求排队等待锁偶发超时。 特征线程栈大量 WAITING/BLOCKED。3.数据库相关慢查询SQL 偶尔走全表扫描缓存失效、统计信息变更索引失效行锁等待并发更新同一行事务持有行锁其他请求阻塞连接池耗尽数据库连接池 maxActive 打满请求排队拿连接主从延迟读从库读到旧数据或者从库压力突增4.中间件抖动Redis / RabbitMQ / ZookeeperRedis大 key、热 key、网络抖动、Redis 持久化 RDB/AOF 阻塞客户端连接池耗尽MQ消费堆积消息处理变慢ZK会话超时重新发起连接5.外部 HTTP 接口调用调用第三方接口偶发响应慢没设置超时、没有熔断当前线程一直等待拖垮接口。6.连接池耗尽数据库、httpclient、dubbo 连接池连接用完新请求排队等待连接等待超时。坑超时只设置业务接口超时没有设置下游调用超时。7.网络问题TCP 丢包、DNS 解析慢、网卡队列满、跨机房网络抖动现象随机超时无固定规律。8.线程池耗尽自定义线程池队列满、线程全部被慢任务占住新任务排队超时。三、排查工具Arthasthread查看线程阻塞trace跟踪方法耗时watch看入参出参SkyWalking/Pinpoint分布式链路追踪精准定位耗时节点jstack抓线程栈看 BLOCKED、WAITING 线程监控GC、连接池、DB 慢日志、Redis 监控tcpdump网络问题抓包四、解决方案JVM优化减少 FullGC设置合理堆大小增加 GC 告警锁缩小锁范围锁内部禁止调用外部接口尽量不使用大锁DB优化 SQL避免行锁冲突合理设置连接池大小连接超时远程调用必须设置超时时间引入熔断降级Sentinel/Hystrix中间件排查大 key、热 key合理设置客户端连接池线程池合理配置核心线程、队列监控线程池队列堆积告警网络跨机房调用评估延迟DNS 缓存高频深挖面试追问Q1接口偶尔超时日志没有异常堆栈优先查什么优先 GC 和分布式链路追踪。GC STW 会暂停业务线程不会打印异常栈其次看线程栈确认是否锁等待、连接池排队。Q2如何区分是本服务 GC 超时还是下游接口慢链路追踪看 span如果本服务内部 span 耗时很长GC / 锁问题如果是调用下游的 span 耗时高下游依赖慢。Q3调用第三方接口没设置超时会怎么样线程会无限阻塞线程池线程被耗尽后续所有请求排队接口批量偶发超时。所有远程调用必须设置超时。Q4数据库行锁导致偶发超时是什么现象并发更新同一行数据事务执行时间长其他事务等待行锁没有并发时接口很快高并发才偶发超时。Q5连接池满导致超时怎么快速定位监控连接池活跃连接数看超时瞬间活跃连接达到 max线程栈大量卡在获取连接的代码。一句话背诵总结接口偶发超时重点排查 GC 停顿、锁等待、数据库慢 SQL / 行锁、中间件抖动、远程调用无超时、连接池 / 线程池耗尽优先链路追踪定位耗时节点超时瞬间抓取线程栈远程调用强制设置超时引入熔断降级增加各项指标监控告警。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑