简介:这份PDF资料聚焦IntelliJ IDEA的Debug调试技巧,面向Java后端开发、Web工程调试人员以及需要提升排错效率的初中级程序员。内容以实际Web工程为例,系统梳理断点设置、Debug模式启动、单步执行、查看变量值、跳过方法与重新开始执行等核心操作,并补充F7、F8、F9、Shift+F8、Alt+F8等常用快捷键的适用场景,帮助读者在真实请求链路中快速定位问题。资源包内共1个PDF文件,大小约828KB,篇幅精炼,适合作为日常调试的速查手册或团队内部培训材料。目前已有5140人学习下载,说明其内容具备一定实用参考价值。通过阅读,读者可掌握从断点触发到表达式求值的完整调试思路,理解进入方法体与跳出方法体的区别,并借助Postman请求示例理解前后端联调中的断点验证流程,从而减少无效日志排查,提升问题定位速度。
1. 断点打了一排,为什么程序还是跑飞了
IntelliJ IDEA 的 Debug 调试技巧,很多人以为就是记住 F8、F7、F9 几个快捷键,真到线上问题复现、接口参数对不上、多线程偶发死锁的时候,才发现断点打了一排,程序该跑飞还是跑飞。我见过太多同事在for循环里无脑 Step Over,一个十万次迭代的循环点到手酸,也见过有人把断点打在 getter 上,结果整个应用卡成幻灯片。这篇小结不讲快捷键大全,讲的是我在真实项目里反复用、能直接抄作业的调试路径:条件断点怎么设、远程 debug 怎么接、多线程怎么盯、断点失效怎么排查。适合已经能跑起来 Java 项目、但调试还停留在「打点看变量」阶段的开发者,也适合想把 IDEA 调试能力榨干的老手对照检查自己的习惯。
2. 断点不只是红点:条件、日志与异常断点的选型
2.1 条件断点:把循环里的第 500 次留给断点
最常见的翻车场景:一个List有几千条数据,你只想看id == 9527那条的处理逻辑。无脑 Step Over 是血泪教训,正确做法是右键断点,在 Condition 里写布尔表达式。IDEA 会在每次到达该行时求值,只有为 true 才暂停。
// 假设循环体里有个 user 对象 for (User user : userList) { // 在这一行打断点,右键 -> Condition 填: // user.getId() == 9527L process(user); }条件表达式里可以调用方法,但要注意副作用。我一般只写纯判断,比如user.getName().equals("admin"),避免在条件里改状态。如果条件里抛异常,IDEA 默认会暂停并提示,这时候看 Evaluate 窗口就能定位。参数上,条件断点对性能有影响,每次命中都要求值,所以别在百万级循环里写复杂表达式,能提前用日志过滤就先用日志。
2.2 日志断点:不暂停也能留下痕迹
有些场景不能暂停,比如复现一个时序敏感的 bug,一停就再也复现不出来。这时候用 Log 断点(右键断点,取消 Suspend,勾选 Evaluate and log)。它不会挂起线程,只在控制台输出你写的表达式。
// Log 断点表达式示例,输出线程名和当前订单号 "thread=" + Thread.currentThread().getName() + ", orderId=" + order.getId()这个技巧在排查「偶发空指针但不知道哪条数据触发」时特别有用。注意日志断点依然会求值,表达式别写太重。另外,IDEA 的日志断点输出格式是固定的,想加时间戳得自己拼System.currentTimeMillis()。
2.3 异常断点:让 JVM 在抛异常的那一行停下
很多人调试异常靠猜,其实 IDEA 有 Exception Breakpoints。在 Debug 窗口点 View Breakpoints,加一个 Java Exception Breakpoint,填NullPointerException,再勾选 Catch class 过滤。这样任何地方抛出 NPE,都会精确停在抛出点,而不是在 catch 块里。
这里有个坑:如果框架内部大量抛异常做流程控制(比如某些老框架用异常判断分支),异常断点会频繁触发。解决办法是加条件,比如只在特定类里触发,或者用instanceof过滤异常消息。我一般会先开异常断点跑一遍,把噪音类的包名记下来,再加过滤条件。
2.4 断点类型对比与选择
| 断点类型 | 是否暂停 | 适用场景 | 性能影响 |
|---|---|---|---|
| 行断点 | 是 | 常规逻辑检查 | 低 |
| 条件断点 | 是 | 循环/高频调用中定位特定数据 | 中,每次命中求值 |
| 日志断点 | 否 | 时序敏感、不能暂停 | 中,每次命中求值 |
| 异常断点 | 是 | 定位异常抛出源头 | 低,但可能噪音大 |
| 方法断点 | 是 | 想知道谁调用了这个方法 | 低,但会拖慢方法入口 |
选型原则:能日志不暂停,能条件不循环,能异常不猜。方法断点我一般只在排查「这个方法到底被谁调的」时临时开,用完就关,因为它对性能影响比行断点大。
3. 远程 Debug:把本地断点打到测试环境上
3.1 启动参数怎么加
远程 debug 的核心是 JVM 的 JDWP 协议。被调试的应用启动时加参数:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar app.jar参数说明:transport=dt_socket用 socket 通信;server=y表示应用作为调试服务端;suspend=n表示启动时不挂起,如果想调试启动阶段就改成y;address=*:5005监听所有网卡的 5005 端口,生产环境千万别这么写,测试环境也要限制来源 IP。JDK 9 以后address写法有变化,老版本用address=5005即可。
3.2 IDEA 里怎么配 Remote JVM Debug
Run -> Edit Configurations -> 加一个 Remote JVM Debug。Host 填测试机 IP,Port 填 5005,Use module classpath 选你的项目模块。点 Debug 后,IDEA 会连上去,控制台出现Connected to the target VM就成功了。
这里有个常见坑:本地代码和远程代码版本必须一致,否则断点行号对不上,IDEA 会提示No executable code at line。我一般要求远程包和本地分支的 commit id 一致,或者至少把远程的 class 文件拉下来对比。另一个坑是防火墙,5005 端口不通就连不上,先用telnet测一下。
3.3 远程 Debug 的边界与安全
远程 debug 会显著拖慢应用,因为每次断点命中都要走网络同步。测试环境可以接受,生产环境绝对不要开,除非是紧急问题且能接受短暂停服。如果非要上生产,至少做到:端口不对外暴露、用完立即关闭、设置suspend=n避免启动挂起。我自己的习惯是,远程 debug 只在预发环境用,且开之前先在群里吼一声,避免别人以为服务挂了。
4. 多线程与并发场景:断点为什么只停了一个线程
4.1 线程断点与挂起策略
默认情况下,IDEA 的断点会挂起所有线程(Suspend All),这在多线程调试时会导致其他线程也停住,有时候你只想看当前线程。右键断点,把 Suspend 改成 Thread,就只挂起当前线程。这个设置在排查线程池任务时特别有用,比如你想看某个 worker 线程处理任务的全过程,又不希望其他 worker 被冻住。
但要注意,如果多个线程同时命中同一个断点,IDEA 默认只停第一个,后面的会被阻塞。想同时看多个线程,得在 Breakpoints 里勾选All或者用Thread模式配合线程过滤器。
4.2 用线程过滤器锁定目标线程
在断点设置里有个Filter选项,可以按线程名过滤。比如线程池名字是pool-1-thread-3,你只想在这个线程里停,就填pool-1-thread-3。支持通配符,比如pool-1-thread-*匹配所有 worker。
// 给线程起个有意义的名字,调试时能省很多事 ThreadFactory factory = r -> { Thread t = new Thread(r); t.setName("order-processor-" + t.getId()); return t; };这个习惯我强烈建议养成:线上线程名默认是pool-1-thread-1这种,出了事根本不知道是谁。自己起名后,调试和 jstack 都一目了然。
4.3 死锁与等待链的排查
多线程调试最怕死锁。IDEA 有个隐藏功能:Debug 窗口点Threads标签,再点Detect Deadlock,它会自动分析线程等待链并高亮死锁。但前提是你能复现死锁并让程序停住。我的做法是,怀疑死锁时先打一个方法断点或异常断点,让程序在可疑区域暂停,然后手动触发死锁检测。
如果死锁是偶发的,远程 debug 也难复现,那就别硬调,直接上jstack抓线程转储,拿回本地用 IDEA 的 Analyze Stack Trace 功能分析。调试工具是组合拳,不是只有断点一条路。
5. 断点失效与常见翻车排查
5.1 断点变成灰色带斜杠
现象:断点图标上有个斜杠,IDEA 提示No executable code at line。原因通常是本地代码和运行的 class 不一致,或者断点打在了注释、空行、接口方法上。解决:先 Build -> Rebuild Project,再检查 Run 配置里的 classpath 是不是指向了旧的输出目录。如果是 Maven 多模块,确认依赖模块的 jar 是不是最新的。
5.2 断点命中但变量显示Collecting data
现象:停在断点后,Variables 窗口里对象显示Collecting data或null,等半天出不来。原因通常是对象的toString()方法里有耗时操作,或者 IDEA 在尝试调用 getter 时触发了懒加载。解决:在 Variables 窗口右键,取消Enable toString(),或者用 Evaluate Expression 手动取字段。我一般会关掉自动 toString,需要看的时候再手动调。
5.3 远程 Debug 连不上
现象:IDEA 报Unable to open debugger port或Connection refused。原因:端口不通、参数写错、JDK 版本不匹配。解决:先在测试机上netstat -anp | grep 5005确认监听,再从本地telnet ip 5005测连通性。如果 JDK 8 和 JDK 11 混用,注意address参数写法差异。另外,有些容器环境需要把 5005 端口映射出来,别只在 Dockerfile 里加参数忘了-p。
5.4 条件断点不生效
现象:条件断点写了表达式,但程序还是每次都停。原因:条件表达式返回了非布尔值,或者表达式里有语法错误被 IDEA 忽略了。解决:在 Evaluate Expression 里先单独跑一遍条件表达式,确认返回true/false。另外,如果条件里用了==比较对象,记得那是引用比较,不是值比较,该用equals就用equals。
5.5 调试时应用卡死或重启
现象:一开 debug 应用就无响应,或者直接重启。原因:断点挂起了关键线程(比如心跳线程、GC 线程),或者远程 debug 参数导致 JVM 启动挂起。解决:检查断点的 Suspend 策略,把Suspend All改成Thread;远程 debug 时suspend=n;如果是单片机或嵌入式场景,注意 debug 模式可能触发看门狗复位,这跟 JVM 调试是两码事,别混为一谈。
6. 把调试变成可复用的验证习惯
调试技巧的终点不是记住多少快捷键,而是形成一套可复用的验证路径。我现在遇到一个新问题,会先问自己三个问题:能不能用日志断点不暂停地观察?能不能用条件断点缩小范围?能不能用异常断点直接定位抛出点?这三个问题能解决八成场景,剩下的两成交给远程 debug 和多线程分析。
再分享一个我自己的习惯:每次调完一个复杂 bug,把关键的断点配置导出成 XML(Breakpoints 窗口有 Export 按钮),存到项目文档里。下次类似问题直接导入,省得重新配条件。这个习惯坚持了两年,现在团队里新人遇到老问题,先翻断点配置库,比翻聊天记录快得多。
最后说一个容易被忽略的点:IDEA 的 Evaluate Expression 窗口支持多行代码和局部变量修改。调试时想临时改个值验证分支,不用改源码重启,直接在 Evaluate 里写user.setStatus(2)然后继续跑。这个技巧在验证边界条件时特别省时间,但记住,改完的值只在当前调试会话有效,别指望它持久化。
调试这件事,工具是死的,场景是活的。希望这些踩坑经验能帮你少走点弯路,把时间花在真正难的问题上。
本文还有配套的精品资源,点击获取