Java8中的Stream和OutputStream是对集合(Collection)对象功能

Java中各种IDE的Debug功能都是通过Java提供的Java Platform Debugger Architecture(JPDA)来实现的。

借助调试功能,您可以轻松调试程序并快速模拟/查找程序中的错误。

虽然 Interllij Idea 的 Debug 功能看起来和 Eclipse 差不多,但在体验上还是比 Eclipse 好很多。

在Debug中,最常用的是下一步,下一个断点(Breakpoint),以及查看运行值的几个操作;但是除了这些IDE之外,它还提供了一些“高级”的功能,可以帮助我们更方便的调试。

Java8 流调试

Stream 作为 Java 8 的一大亮点,与 java.io 包中的 InputStream 和 OutputStream 是完全不同的概念。Java 8中的Stream是对集合(Collection)对象功能的增强,专注于对集合对象进行各种非常方便高效的聚合操作(aggregate operations),或者批量数据操作(bulk data operations)。

IntStream.iterate(1, n -> n + 1)                .skip(100)                .limit(100)                .filter(PrimeFinder::isPrime)//检查是否是素数                .forEach(System.out::println);复制代码

上面的代码是流、排序集合和转换值的常见用法。Idea还提供了分析流过程的能力

修改程序执行流程

在调试过程中,一般情况下程序是可以正常执行的。但是,在某些情况下,需要动态修改执行过程。这个时候修改代码太不方便了。好在Idea提供了一些函数来动态修改程序的执行过程,让我们可以灵活调试。

返回上一个堆栈帧/删除当前堆栈帧/“丢帧”

当我们在Debugging时遇到手抖,提前或按错下一步,导致漏断断点。此时可以通过Idea提供的Drop Frame函数返回到之前的栈帧

虚拟机栈描述了Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)【图解】来存储局部变量表、操作数栈、动态链接、方法出口等信息, 等等。 。每个方法从调用到执行完成的过程,对应一个栈帧被压入虚拟机栈中的栈的过程。

其实不光是Java,其他编程语言的方法执行模型也是栈结构。方法的执行对应于 push/pop 操作。

比如下面的代码,当方法执行一次时,栈帧上有两个方法

此时点击Drop Frame按钮后,栈顶的数据会被删除,回到调用log方法之前的位置。

注意:虽然 Drop Frame 好用,但是 Drop Frame 之后可能会出现一些不可逆的问题伪代码用什么软件写,比如 IO 类操作,或者修改后的共享变量不能回滚,因为这个操作只会删除栈顶的栈帧伪代码用什么软件写,而不是真的“倒退”

强制返回

当一个方法比较长,或者 Step Info 到达一个不太重要的方法,想跳过该方法时,可以使用 Force Return 函数来强制结束该方法

注意:Force Return 与 Step Out 不同。Step Out 跳出当前步骤或执行方法中的代码;而Force Return则直接强制方法结束,跳过方法后面的所有代码,直接返回。比如下面的代码,当使用Force Return时,evaluate方法中的println不会被执行

当要强制返回的方法有返回值(非void)时,强制返回也需要指定返回值

触发异常

当被调用的方法可能抛出异常,调用者需要处理异常时,该方法可以直接抛出异常,无需修改代码

下面是模拟发送请求并随着时间的推移自动重试的伪代码

方法执行到sendPacket时,可以进行Throw Exception操作,提前结束方法,抛出指定的异常

调用者收到异常后,可以执行catch中的重试逻辑,这样就不用修改程序来模拟异常,非常方便

调试正在运行的 JVM 进程(附加到进程)

当应用程序无法在 Idea 中运行,而您想调试正在运行的程序时,可以使用 Attach to Process 功能,该功能可以调试正在运行的程序。当然前提是要保证运行的JVM进程代码和Idea中的代码一致

这种情况其实很常见。比如想调试springboot可执行jar,或者调试tomcat源码等独立部署运行的进程,使用Attach to Process就非常方便。您可以使用 Idea + Idea 以外的环境。调试代码

其实这个功能在C/C++ GDB下也是可以的。它只是Debug的运行程序,Intellij Clion也支持。

远程调试

远程调试是JVM提供的一个功能,和上面的Attach to Process类似,只是进程从本地变成了远程

比如我们的程序本地没有问题,但是服务器有问题;比如本地是MacOs,服务器是Centos,由于环境不同会出现一些bug。这时候我们就可以通过远程调试功能进行调试了。

如果要开启远程调试,需要在远程JVM进程的启动脚本中添加如下参数:

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005复制代码

挂起参数表示JVM进程是否已经以“挂起”模式启动。如果它以“挂起”模式启动,JVM 进程将被阻塞并且不会继续执行,直到远程调试器连接到该进程。

这个参数很有用,比如我们的问题出现在JVM启动过程中(比如Spring的加载/初始化过程),我们需要设置suspend为y,这样JVM进程才会等待Ide中的远程调试连接完成之前继续运行。不然远程JVM运行了一段时间,Ide的Debugger才连接上,早就错过了断点。

远程JVM进程配置为Debug模式并启动后,可以连接Idea,在Idea的Run/Debug Configurations面板中新建一个Remote Configuration:

然后配置Host/Port,点击Apply保存

最后先启动远程JVM进程,然后在Idea中Debug运行刚才配置的Configuration

温馨提示:远程调试下,由于网络开销,响应会变慢,会导致远程程序挂起。请找一个没有人使用的环境。

多线程下调试

多线程程序很难编写,准确地说,它们也很难调试。由于线程安全问题,一个粗心的进程会导致各种错误,并且这些错误可能难以重现。由于操作系统的线程调度是我们无法控制的,所以多线程程序的错误非常随机,一旦出现问题就很难发现;我们的程序可能在 99.99% 的情况下 OK,但是最后的 0.01% 也很有可能导致严重的错误

线程安全最常见的问题是竞争条件,当多个线程同时修改某些数据时可能会出现这种情况

比如下面的流程,一般情况下,程序是没有问题的

当出现竞争问题时,在单个线程的读写操作之间调度其他线程,数据就会出错。

以下是示例代码。共享数据a虽然是一个synchronizedList,但并不能保证addIfAbsent是原子操作,因为contains和add是两个同步方法,两个方法之间的执行间隙仍有可能被其他线程修改。

import java.util.ArrayList;import java.util.Collections;import java.util.List; public class ConcurrencyTest {    static final List a = Collections.synchronizedList(new ArrayList());     public static void main(String[] args) {        Thread t = new Thread(() -> addIfAbsent(17));        t.start();        addIfAbsent(17);        t.join();        System.out.println(a);    }     private static void addIfAbsent(int x) {        if (!a.contains(x)) {            a.add(x);        }    }}复制代码

如果调试这段代码,在 Step Over(下一步)之后,下一步的范围是整个进程,而不是当前线程。也就是说,经过下一步Debug之后,很可能会被其他线程插入和修改。这个共享数据a也是不安全的,很可能会出现重复添加元素17的问题。

但是,上述问题只是可能的,在实际调试中很难重现。Idea的Debug可以将暂停粒度设置为线程,而不是整个进程

Suspend设置为Thread后,如下图,在a.add这一行打断点,然后在Debug模式下运行程序,主线程和新创建的线程都会在addIfAbsent方法中挂起,我们可以在面板中的 Idea Switch 线程中调试

此时主线程和子线程都调用了contains方法,都返回false,挂在a.add这一行,准备给a加17。

执行下一步后,主线程成功将17添加到集合中

这时候切换到Thread-0线程的时候,依然挂在a.add(x)这一行,但是set a中已经有元素17了,但是Thread-0线程会继续添加,set a就会添加后重复。元素 17,导致程序中的错误

从上面的例子可以看出,在调试多线程程序的过程中,可以使用Idea Debug的Suspend功能来模拟多线程竞争的问题,非常方便编写或调试多线程程序.

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论