最好的IDEA debug长文?看完我佛了(下)

简介: 最好的IDEA debug长文?看完我佛了(下)

快速计算表达式


都知道调试面板里的Evaluate Expression可以计算表达式/变量的值,但那毕竟还得弹个窗稍显麻烦,其实还有更为方便的方式:


image.gif

用鼠标操作,效率指数级提升。这个操作方式是:鼠标指针选中表达式(IDEA智能自动选中) + 鼠标左键单击。当然喽,如果你想执行自定义的不存在于代码中的表达式,那必须调起窗口来操作。


Stream流调试


Java 8的流行,彻底让流式编程走进我们的视野。使用Stream编程的好处众多,但一直被大家诟病的是难以阅读和难以调试,特别是后者。


为了调试它,我们经常需要插入其它断点,并分析流中的每个转换,不可为不麻烦。还好IDEA提供了处理该痛点的“能力”:当调试器在Stream API调用链之前或之内停止时,点击Trace Current Stream Chain这个图标即可以“非常好看”的图形化方式展示出来,一目了然:


20210129172853866.gif


主动抛出异常


需求场景:你写了一个全局异常组件,现在想测试它生效情况如何,那么时候你就需要主动抛出这种异常,一般的做法是形如这样:


// 自己在程序内主动throw一个
throw new NullPointerException();
// 或者构建个表达式
int i = 1/0;


这种做法均有一定的代码侵入性,用后还得删除。其实IDEA还提供了一种更为优雅的解决方案:

20210129173610206.gif


掌握了IDEA断点调试的基本技能,下面进入到本文深水区:断点类型。难度不高,依旧是使用层面的事,但由于很多同学并不知道,因此是你用于超车的好材料。


四大断点类型


对于打断点,估计大部分同学都只会左边鼠标单击这种最基础的方式。所以,看到这个小标题估计你得再懵一次吧。what?断点还有种类?


若你也是只在代码左边鼠标单击打上“小红点”,然后嘎嘎就是干,空中转体720度向后翻腾三周半…一把唆的选手,那么接下来就坐稳喽,准备发车。


这么个姿势也许能帮你定位50%以上的问题,但还有另外一半的case呢?如for循环调试,Stream流调试,lambda调试、异常调试等这些场景,用那“一把唆”的方式就很难搞定甚至说搞不定了。断点是帮我们快速定位问题的,不同的场景打上合适的断点将能事半功倍。


殊不知,IDEA给我们开发者提供了非常的断点类型,以应对不同场景下的调试。在对应的场景下使用合适正确的断点类型,能够大大提高调试的效率,从而别人加班你下班,效率就是时间,而时间就是生命。


image.png


如图,IDEA把断点分为四大类型(截图中只有三类):


  1. Line breakpoint(行断点):图中红色小圆圈。顾名思义,在指定代码行设置断点
  2. Field watchpoint(属性断点):图中红色小眼睛。打在类的属性(static or 非static)上的断点,作用是在该属性读取和写入时激活
  3. Method breakpoint(方法断点):图中红色小菱形。标记在方法签名的那一行,在该方法执行的入口/出口处被激活
  4. Exception breakpoint(异常断点):红色小闪电。这是一个特殊但很好用的断点,当程序抛出指定异常时会激活异常断点。和其它断点不同,异常断点是项目全局的,它不需要打在具体某一行上


下面就到了“啃硬骨”的时候了,来吧。


行断点Line breakpoint


使用得最最最广泛的断点类型,平时大部分情况下都使用此种断点。


image.gif


从“教程”中可以看到该断点有很多的设置项,也就是有很多的断点参数可以配置,来了解下。


断点参数


因为这是第一个介绍断点参数的类型,因此会说得详细些,这样子后面相同功能的参数就不用再赘述了。对照这个截图页:


image.png


  • Enabled:不解释。但需注意:若此项不勾选上,小红点并不会消失,而是由实心的变为空心的,当然喽,一般情况下并不会动此项
  • Suspend:众所周知,断点激活时会阻塞程序的继续运行,从而阻塞当前线程。但是当你发现它是个复选框的时候,有没有被诧异到?并且,并且,并且你还可以根本就不勾选它,有何区别:
  • 若不勾选选中:此断点相关活动(如打日志等)依旧正常进行,只是不阻塞进程了
  • 若勾选中:
  • All(默认):阻塞该程序内所有线程
  • Thread:只阻塞当前断点所在线程


image.gif


如上图,不勾选Suspend:线程14和线程15正常运行,“畅通无阻”


image.gif


如上图,勾选Suspend-All:在断点处,所有线程都被阻塞了,统一给我等待。


image.gif


如上图,勾选Suspend-Thread:method1的线程被阻塞,但是并不影响另外一个线程调用method2。


试想一下,既然“勾选Suspend-Thread”影响更小,那为何IDEA默认帮你选择All而不是Thread呢?原因是这样子的:调试的目的就是让程序“慢下来”,最好是静止下来方便分析问题。否则,其它线程如果仍旧继续保持执行的话,可能一会这个请求改掉这个数据一会改掉那个数据,增加了不可控性。不确定的增加从而大大增加调试难度和定位问题的难度,所以索性上个“同步锁”来得省心,因此默认选中Suspend-All是合理为之。


说明:很多时候我们需要用本机连接测试环境打断点进行远程调试,若在这个case下强烈建议你使用Thread模式,否则你懂的


  • Condition: 断点被激活的条件。你可以在此处书写表达式,只有表达式返回true时此断点才会被激活
  • 条件断点严格来讲不属于一种断点类型,属于断点参数决定的,很多类型的断点都可加条件


image.gif


  • Log:它有三个选项,是checkbox哦。也就是说可都选,也可都不选,默认一个都不选
  • Breakpoint hit message:断点激活时输出提示日志
  • Stack trace:断点激活时输出程序调用栈信息
  • Evaluate and log:选择需要输出计算表达式的值。你可选择当前可达的变量,如本例的main函数入参args等
  • remove once hit:断点激活一次后就立马给移除喽,也就是所谓的临时行断点,下面来介绍下它


还有窗口里最右边的这块条件


image.png


见名之意,一系列过滤器:过滤实例、过滤类、过滤调用者等等,一般这些们几乎不会使用(至少我目前是还没用过的),所以就一笔带过。


使用场景


行断点一般配合单步调试一起使用,在看框架源码、定位基础问题等使用得特别多,是最需要掌握的一种断点类型,没得商量。


临时行断点Temporary line breakpoint


它也属于行断点的一种,只是参数不一样而已。由于它比较特殊,所以单摘出来说道说道。创建普通行断点,然后把Remove once hit复选框勾选上即是一个临时行断点,效果如下:


image.gif


这种断点类型,实际使用场景其实很少。


属性断点Field watchpoint


此类断点是打在属性上的,成员属性和静态属性均可。它不是小红点,而是个红色“小眼睛”。


20210128141001682.gif


断点参数




如图,此种断点类型特有个watch参数,两个可选值的含义为:


  • Filed Access:读取此属性时(写入时不管)
  • Filed madification:写入此属性时(读取时不管)


使用场景


当想知道xxx属性的赋值是谁时,由于程序太庞杂没法知道断点打哪儿从哪开始跟踪,这个时候使用属性类型的断点一下子就搞定了,非常的方便。


方法断点Method breakpoint


断点必须打在方法签名的那一行,颜色形状是个红色的小菱形。


20210129065143736.gif


断点参数


image.png


Watch有三个可选值:


  • Emulated:仿真。作用:提高调试性能,因此默认情况下使用。官方建议:仅在调试远程代码或在没有行号信息的native方法或类中设置断点时,才建议禁用此选项
  • Method entry:进入方法时激活断点
  • Method exit:出去方法时激活断点


若entry和exit都勾选,那在进入之后和出去之前都会激活断点


使用场景


对于此种断点类型,可能你会说没啥卵用。毕竟自己在方法头尾打个行断点就能达到同样效果,没必要单独搞个类型嘛。


其实,它的杀手锏级使用场景是把此种类型断点打在接口方法上,这样子不管哪个实现类方法被调用,都会激活断点,是不是特别给力。


异常断点Exception breakpoint


比较小众,但并不代表不重要。在我理解它比较小众,可能大多数同学不知道如何打一个异常断点,因为它不是鼠标单击就能轻松搞定。


上面介绍了异常断点它是一种全局断点类型,因此并不能在代码处直接单击,而是只能在管理窗口里统一添加:


image.gif


和其它断点类型相比,至少有如下不一样:


  1. 创建断点只能通过断点管理窗口创建,而不能通过鼠标点击方式
  2. 创建完成后,代码栏处不会有任何显示(没有红色小图标),直到它被激活时才会出现红色小闪电
  3. 异常断点作用于全局:本例中任何地方抛出了NullPointException都会激活此断点


断点参数


image.png


Notification有两个可选值:


Catch excetion:只有当你自己try-catch了这个异常才会激活断点

Uncatch excetion:只有当你自己不try-catch时才会激活断点

默认情况下这两个都会被勾选上,也就是说任何情况下发生此异常,都会激活断点。


使用场景


知晓了异常断点的作用和触发条件,使用场景就有啦。比如当你的程序抛出了一个异常,但是一时半会你并不知道是哪行代码引起的,这个时候通过增加异常断点的方式可以实现迅速的问题定位。


4种断点图标对比


每种断点类型都有自己对应的图标,且有不同的状态。我从官网趴了一张对比图,总结得特别好,在这里一并分享给你:


image.png


远程调试(远程Debug)


现在大都是微服务架构方式,每个微服务一般会有N多个上/下游依赖,如此以至于给调试带来了很大困难,毕竟你几乎不可能在本地同时把依赖都启起来用IDEA做调试。所以,远程调试来了,它是调试分布式系统的一个利器。


远程调试:顾名思义,使用本地IDEA调试远程代码(一般为QA环境,线上环境不可能开启调试端口的)。那么如何开启远程调试呢?


开启步骤


开启远程调试只需要两步即可:


第一步:让远程部署的那个应用支持远程调试,也就是暴露远程调试端口。方式方法为在应用启动时加上对应的JVM参数即可,JDK版本不同参数也不一样


  • JDK 9+:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*😒{debug_port}
  • JDK 5-8:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=${debug_port}
  • JDK 4:-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${debug_port}
  • JDK 3-:-Xnoagent -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${debug_port}


第二步:用IDEA创建一个remote运行配置,填上远程主机的ip + 暴露的调试端口即可。操作路径为:Edit Configurations -> Add New Configuration ->


image.png


万事俱备,点击debug运行,控制台里能看到如下字样就证明你链接成功了:


image.png


值得注意的是:远程调试时请确保你本地代码和远程代码一模一样,以达到最佳效果。


传统Tomcat如何开启远程调试?


若你是个Spring Boot应用,那么在jar -jar时加上JVM参数即可,那么如果是要使用传统的tomcat方式部署呢?这个时候找到传统tomcat的启动脚本startup.sh:


#!/bin/sh
os400=false
...
PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh
...
exec "$PRGDIR"/"$EXECUTABLE" start "$@"


为了加上咱们的JVM参数,只需要在exec xxx之前添加一个变量值即可(以JDK8为例):

JPDA_OPTS='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=具体的端口号


注意:这个key名称必须是JPDA_OPTS。


有好奇心的你可能不禁就要问了:为何加个JPDA_OPTS参数就行了呢?也没见exec xxx使用它呀,其实不然,下面简单解释下,不展开。


exec执行时引用了变量 $EXECUTABLE,它代表的是就是catalina.sh这个文件,该文件里面有大量变量判断脚本,当然包括负责对JPDA_OPTS解释:

#!/bin/sh
cygwin=false
darwin=false
...
if [ "$1" = "jpda" ] ; then
  if [ -z "$JPDA_TRANSPORT" ]; then
    JPDA_TRANSPORT="dt_socket"
  fi
  if [ -z "$JPDA_ADDRESS" ]; then
    JPDA_ADDRESS="localhost:8000"
  fi
  if [ -z "$JPDA_SUSPEND" ]; then
    JPDA_SUSPEND="n"
  fi
  if [ -z "$JPDA_OPTS" ]; then
    JPDA_OPTS="-agentlib:jdwp=transport=$JPDA_TRANSPORT,address=$JPDA_ADDRESS,server=y,suspend=$JPDA_SUSPEND"
  fi
  CATALINA_OPTS="$JPDA_OPTS $CATALINA_OPTS"
  shift
fi
...


关于JVM调试平台JPDA更多知识点,可自行用谷歌百度一下学习学习


嵌入式Tomcat如何开启远程调试?

这不就是Spring Boot应用形式麽?所以,如何开启,不用再废话了吧~


总结


人和动物的最大区别之一是人会使用工具,且善于使用工具。工具被创造出来,使命就是提效的,毕竟我们不可能用记事本去写Java程序吧。


IntelliJ IDEA作为最为流行的JVM平台IDE,我们应该尽可能的去挖掘出它的效用,既然作为集成开发环境,其实很多功能都可以一站式搞定,在一个平台里做很多数据都能打通。比如IDEA的rest接口调试、数据库映射、Shell终端等等,应付平时的开发一般搓搓有余,推荐使用,毕竟软件启得越多电脑越卡不是。


用IDEA和会用IDEA是两个层次,除了代码本身,最常用的开发工具也是值得花番心思的。大道至简,知易行难,知行合一,得到功成!


本文思考题


本文所属专栏:IDEA,后台回复专栏名即可获取全部内容,已被https://www.yourbatman.cn收录。


看完了不一定懂,看懂了不一定会。来,文末3个思考题帮你复盘:


  1. 断点能打在类上吗?
  2. IDEA能设置哪几种类型的断点呢?各有什么场景?
  3. 如何用IDEA debug调试测试环境的应用?
相关文章
|
6月前
|
Java 内存技术
IDEA工具debug的小技巧
IDEA工具debug的小技巧
69 0
|
6月前
|
Java
IDEA debug HashMap源码的心得
IDEA debug HashMap源码的心得
63 0
|
4月前
|
Java
idea远程debug应用
idea 中debug现场部署的Java应用
35 1
|
4月前
Idea 进行远程服务器debug操作
Idea 进行远程服务器debug操作
434 0
|
5月前
|
Java Spring
idea Spring-boot 项目debug启动过慢 spring debug启动过慢解决办法:已解决
idea Spring-boot 项目debug启动过慢 spring debug启动过慢解决办法:已解决
187 3
|
5月前
idea远程调试debug
配置idea远程debug
28 0
|
5月前
|
监控 前端开发
IDEA Debug技巧大全,看完就能提升工作效率
IDEA Debug技巧大全,看完就能提升工作效率
71 0
|
6月前
|
Java
IDEA DeBug
IDEA DeBug
43 1
|
6月前
|
Java 测试技术
使用IDEA进行服务器远程debug调试
使用IDEA进行服务器远程debug调试
122 0
|
6月前
IDEA远程调试Remote Debug
IDEA远程调试Remote Debug
56 0