- 在程序出现问题时,查看参数变化以及方法的调用。
- 查看参数结构
- 查看方法调用以及参数变化
先讲一个开发人员经常会遇见的现象~
A和B两个developer共同负责同一个项目P的开发,P在dev环境上只部署了一台机器。有一天,A需要远程调试P的接口1,于是他使用本地idea启动remote连接到了P,debug的不亦乐乎。而此时B正在调用这台机器的接口2,B突然发现刚才还好好的,突然就不能访问了(B一脸懵逼样)……
听完了故事,下面我们进入主题~
开发人员经常会使用到本地debug功能,有时候有场景需要远程debug日常环境的机器,在这种情况下可能会有多个人同时在使用这台机器,经常出现的现象是某一个人在远程debug这台机器,导致其他人一直在等待。而其他人也是一脸懵逼,不知道这台机器到底发生了什么……
本文的目的是站在debug操作者的角度,探讨如何最小化的避免自己远程debug时对其他人造成影响。
**实践
- Suspend 设置为 Thread (设置为默认 : Make Default)
- Condition 根据该断点上方的变量,编写只对自己生效的代码。
多人同时远程Debug冲突解决方案
是否可用。标识该断点是否生效。优先级最高。
- :对整个java应用生效。程序运行到这个断点时,其他的线程都会停止,直到这个断点放开。
- :仅对当前线程生效。程序运行到这个断点时,不影响其他的线程。
后端开发调试某个数据的时候,前端总是嫌弃后端断点,影响到他开发,这时候我们就可以使用thread级别。
可以编写断点生效的条件。
JVM 直接对 Java 栈的操作只有两个,就是对栈帧的压栈和出栈,遵循“先进后出”/“后进先出”原则。
在一条活动线程中,一个时间点上,只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧(栈顶栈帧)是有效的,这个栈帧被称为当前栈帧(Current Frame),与当前栈帧相对应的方法就是当前方法(Current Method),定义这个方法的类就是当前类(Current Class)。
执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
理解:也就是说完整的调用流程为,从栈底到栈顶。
下面写一个简单的代码
输出结果为
满足栈先进后出的概念,通过 IDEA 的 DEBUG,能够看到栈信息
在debug的过程中,我们可以多关注一下调用栈,这样就能清楚的知道当前方法的调用链路。
将光标回到当前断点停顿的地方
执行当前行代码,并将运行进度跳转到下一行。
进入到当前代码行的方法内部。
从方法内部出去
强制进入Java自带方法的内部
将光标定位到想到达的代码行
点击Run to Cursor
丢弃当前虚拟机栈帧
初始:
进入方法:
丢弃当前帧:
也就是说,我们退回了上一步进入方法之前。
可以用它来评估表达式
如 p.getName()等。
我们在调试代码的时候中间出现了异常,但是我们又没有做异常捕获,稀里糊涂地把错误数据存到了数据库中,我们又需要将这些数据给删除,将数据库复原,才能达到之前我们需要的效果。
所以,接下来我们讲一讲如何避免操作资源,强制返回。
debug:
我们发现程序出现了异常
Force Return
它会只打印shit happens,不会继续向下执行了。
左下角平铺模式Flat Mode:
断点:如果把程序想象成一条平滑的线,那么断点就是一个结,可以让程序中断在需要的地方,从而方便其分析。
设置断点:在代码里需要调试的地方,鼠标双击代码行号的左边,再次双击即可取消断点。
在调试中可以设置的断点类型有五种:
- 行断点:
spring在注册Bean定义(registerBeanDefinition)时,如果是org.springframework.demo.MyBean,就挂起线程,可以开始单步调试了。
对于命中次数(hit count)的使用,一般是在循环中,第N个对象的处理有问题,设置hit count = N, 重调试时,可以方便到达需要调试的循环次数时,停下来调试。 - 方法断点:
方法断点的好处是可以从方法方法进入或者退出时停下来调试,类似行断点,而且只有行断点和方法断点有条件和访问次数的设置功能。
但是方法断点还有另外一个好处,如果代码编译时,指定不携带调试信息,行断点是不起作用的,只能打方法断点。
有兴趣的可以将Add line number…前的勾去掉,调试下看看。 - 观察断点:
在成员变量上打的断点。只有对象成员变量有效果,静态成员变量不起作用。
可以设置变量被访问或者设置的时候挂起线程/VM。 - 异常断点:
系统发生异常时,在被捕获异常的抛出位置处或者程序未捕获的异常抛出处挂起线程/VM, 也可以指定是否包括异常的子类也被检测。 - 类加载断点:
在类名上打的断点。接口上是打不了类加载断点的,但是抽象类是可以的,只是在调试的时候,断点不会明显进入classloader中,单步进入知会进入到子类的构造方法中,非抽象类在挂起线程后单步进入就会到classloader中(如果没有filter过滤掉的话)。类加载断点不管是打在抽象或者非抽象类上,都会在类第一次加载或者第一个子类第一次被加载时,挂起线程/VM。
注意:每种断点的设置有些许不一样,可以在断点上右键->Breakpoint properties进行设置,但一般在断点窗口有快速设置的界面,Breakpoint properties中多了filter, 其实比较鸡肋,用处不大。
启动服务开始调试:
- 方法一:例如上图的代码中,鼠标点击main方法-->右键Debug As-->Java Application开始java代码调试;
- 方法二:直接点击“调试”按钮,即点击小瓢虫边上的倒三角,选择Debug As-->Java Application;
- 方法三:快捷键F11;方法四,菜单栏选择Run-->Debug,还有其他方法此处不再赘述了。
开发工具首次调试会弹出提示,需要切换到Debug工作区,勾选“Remember my decision”,下次便不再提示。
调试执行:
在方法调用栈上的某个方法右键,选择Drop To Frame就可以从该方法的开始处执行,比如 重新执行本方法,可以在本方法上用Drop To Frame,将从本方法的第一行重新执行。
当然对于有副作用的方法,比如 数据库操作,更改传入参数的对象内容等操作可能重新执行就不再是你想要的内容了。 Copy Stack 无 拷贝当前线程栈信息
使用鼠标左键点击代码左侧:
右键点击行断点,我们也可以进行一些断点停顿的条件设置:
如 i == 20等条件。
Suspend也可以选择线程模式,我们可以切换不同的线程,来观察不同线程的该语句的运行效果。(如果是All的话,那就是哪一个线程先过来,那就是哪个线程)
SHIFT+鼠标左键:
debug:
方法断点 = 方法起始行断点 + 方法结尾行断点
在方法上打断点:
debug:
第一个断点停留在方法体内第一行代码:
第二个断点停留在方法体内返回的最后一行代码:
在接口方法上打断点:
真正运行的是接口方法的实现类:
如果我们有很多的实现类,我们具体不知道是哪一个,我们只需要在接口方法上打一个断点,它就会自动地跳到接口具体的实现类方法上。
异常断点会停顿在报出异常的具体代码行。
- 点击View Breakpoints
- 在异常断点处添加新的异常断点
- 接下来,只要你的程序遇到空指针异常,它就会停顿到发出空指针异常的那一行代码那里。
没有显式打断点:
debug:
这个异常断点对于我们异常调试很方便。
在类的字段属性上打断点:
我们在字段左边打了一个字段断点(小眼睛),它就会去监控该字段属性的整个生命周期的值的变化。
dubug:
第一个:构造方法修改了属性值
第二个:setter方法修改了属性值
不暂停直接观察相关变量值
可以直接观察到调用堆栈,类似 Arthas 的 trace
如果执行了断点所在位置,会在控制台打出一行日志:
运行原理:
- 远程服务器开启提供调试的端口号
- IDE客户端通过此端口号连接上服务器
- 服务器通知客户端运行到了哪一行
正常情况下:
- 用户访问服务器ip地址
- 服务器返回结果
远程断点调试Debug情况下:
- 用户访问服务器
- 服务器得到了具体运行的那一行
- 然后就会去问一下本地跟它连接的IDEA,问它有没有这一行的断点
- IDEA检查了一下自己的断点列表
- 然后开发人员就开始debug调试
- 结束调试,将结束调试的信息返回给服务器
- 服务器返回运行结果给用户
代码:
点击
点击
选择Attach to remote JVM(依附上远程服务器的端口)
填写远程服务器的ip地址,指定远程服务器和本地IDE进行socket连接进行断点调试的端口号(随意即可)
科普:我们本地debug的时候也是用的socket连接,只不过此时的socket客户端和服务器都是本地而已。
可以看到下面生成了命令行参数,在服务器端使用,上面生成的为命令行参数(如果报错,就将 * 号进行转义处理),这样服务器就启动了。
运行Debug程序
就可以正常Debug了
今天在项目添加了微信支付后,原本以为绝对OK,启动SpingBoot服务,好家伙,直接起不来,控制台也不输出异常信息。一猜就应该是自己手欠,没办法,只能用最朴实的办法解决了,debug。
步骤一
在你的项目启动类的这行代码打一个断点,并复制这行代码
步骤二
同时按下快捷键 Art+F8,然后在框框中输入,如下所示
步骤三
点击按钮 Evaluate,进行分析
然后你会看到对应的错误提示,注意,有的不是在第一层,你需要点击cause这里,层层点击进去,看到没,是不是很熟悉的场景,bean注入失败
我这里是引入了redssion客户端导致的,版本不一致的原因。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/java-jiao-cheng/15975.html