The Debug functions of various IDES in Java are implemented through the Java Platform Debugger Architecture (JPDA) provided by Java.

With the Debug function, you can easily Debug programs and quickly simulate/find errors in programs.

The Debug function of Interllij Idea says that although it looks similar to Eclipse, it is still much better than Eclipse in terms of user experience.

In Debug, the most commonly used is the next, the next Breakpoint, to view the value of several operations. But in addition to these ides, there are some “advanced” features that make debugging easier

Java8 Streams Debug

One of the highlights of Java 8 is Stream, which is a completely different concept from InputStream and OutputStream in the Java.io package. Stream in Java 8 is an enhancement to Collection objects that focuses on all kinds of very convenient and efficient aggregate operations, or bulk data operations, on Collection objects.

IntStream.iterate(1, N -> n + 1).skip(100).limit(100).filter(PrimeFinder::isPrime)// check if it isPrime.Copy the code

The code above is a common use of Streams to sort and convert values from a collection. Idea also provides the capability to analyze streams processes





Modify program execution flow

During debugging, generally, let the program run normally. However, in some cases, the execution process needs to be dynamically modified, and it is still too inconvenient to modify the code. Fortunately, Idea provides some functions to dynamically modify the execution process of the program, which allows us to debug flexibly

Return last stack frame/Drop current stack frame/Drop frame

When we were in Debug, hand shaking and so on, we missed the breakpoint by pressing the next step too early or incorrectly. In this case, you can use the Drop Frame function provided by Idea to return to the previous stack Frame

The virtual machine Stack describes the memory model of Java method execution: each method execution creates a Stack Frame to store information about local variables, operand stacks, dynamic links, method exits, and so on. The process of each method from invocation to completion corresponds to the process of a stack frame being pushed into and out of the virtual machine stack.

In fact, not only Java, but also other programming languages, method execution model is also a stack structure, method execution corresponds to a push/pop operation

For example, in this code, after executing a method once, there are two methods on the stack frame

At this point, clicking the Drop Frame button removes the data at the top of the stack and returns it to where it was before the log method was called

Note: If you Drop a Frame, some irreversible problems may occur. For example, an IO class operation or a modified shared variable cannot be rolled back. This operation only deletes the top Frame of the stack.

Force method Return

When a method is too long, or if you want to skip a Step Info to a less important method, you can use the Force Return function to Force the method to end

Note: Force Return is not the same as Step Out, which is to jump Out of the current Step or execute the code in the method. Force Return is a direct Force to end the method, skipping all the code after the method directly returns. For example, println in the evaluate method is not executed when Force Return is used

Force return also needs to specify a return value when the method to be forced to return has a return value (not void)

An exception

When a called method might throw an exception and the caller needs to handle it, he can simply have the method throw an exception without modifying the code

The following is a piece of pseudo-code that simulates sending a request and automatically retries after timeout

When a method is executed to sendPacket, you can perform a Throw Exception to prematurely terminate the method and Throw the specified Exception

Once an exception is received, the caller can execute the retry logic of the catch, which is convenient without having to modify the program to simulate the exception

Debug running JVM processes (Attach to Process)

If the application cannot run in Idea and you want to Debug the running program, you can use the Attach to Process function, which can Debug the running program on the premise that, of course, Ensure that the running JVM process code is consistent with the code in Idea

Attach to Process this scenario is very common, for example, when you need to debug the SpringBoot Executable JAR, or debug tomcat source code and other independently deployed processes, it is very convenient to Attach to Process. Debug can be done with the environment outside Idea + code in Idea

This functionality is also available in C/C++ GDB, and Intellij Clion also supports debugging of running programs

Remote debugging

Remote debugging is a feature provided by the JVM, similar to Attach to Process above, except that the Process has been changed from local to remote

For example, our program has no problems locally, but has problems on the server; For example, if the local device is MacOs and the server is Centos, some bugs may occur due to different environments. In this case, you can use the remote debugging function to debug bugs

If you want to enable remote debugging, you need to add the following parameters to the startup script of the remote JVM process:

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
Copy the code

The suspend parameter indicates whether the JVM process is started in “suspended” mode. If it is started in “suspended” mode, the JVM process blocks and does not continue executing until the remote debugger connects to the process

This parameter is useful if our problem is occurring during JVM startup (such as Spring’s load/initialization process) and you need to set suspend to Y so that the JVM process waits for the remote debug connection in the Ide to complete before continuing. Otherwise, the remote JVM has been running for a while, and the Ide Debugger has already missed the breakpoint

After the Remote JVM process has configured Debug mode and started, you can connect in Idea by creating a new Configuration for the Remote in the Run/Debug Configurations panel of Idea:

Then set the Host/Port and click Apply to save

Finally, start the remote JVM process first, and then Debug the Idea to run the Configuration

Tip: Under remote debugging, due to the overhead of the network, the response will be slow, and will lead to the suspension of the remote program, please find an environment that no one is using

Multithreading debugging

Multithreaded programs are hard to write, and indeed hard to debug, and a careless thread safety issue can cause all kinds of bugs that can be hard to reproduce. Because the operating system thread scheduling is out of our control, so the error of multithreaded program has a great randomness, once there is a problem is difficult to find; Our program may work 99.99% of the time, but the last 0.01% is likely to cause serious errors

The most common problem with thread-safety is race conditions, which can occur when some data is modified by multiple threads at the same time

Take the following process, which normally works fine

When a race problem occurs, and other threads are scheduled between read and write operations on a single thread, data errors occur

Here is an example of code that, although shared data A is a synchronizedList, does not guarantee that addIfAbsent is an atomic operation because contains and add are synchronized methods. It is still possible for the two methods to be modified by other threads between their execution

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); }}}Copy the code

If you Debug this code, after a Step Over, the scope of the next action is the entire process, not the current thread. That is, after the next step of Debug, it is very likely that other threads will insert and perform the modification, the shared data A is also unsafe, and it is very likely that element 17 will be added repeatedly

However, the above problems are only possible and difficult to reproduce in actual debugging. Debug of Idea can set suspend granularity to threads rather than the entire process

After setting Suspend to Thread, as shown in the figure below, and setting a breakpoint on the a.dd line, and then running the program in Debug mode, both the main Thread and the new Thread are suspended in the addIfAbsent method. We can switch threads in the Debug panel of Idea

At this point, both the Main thread and the child thread have called the contains method, and both return false, suspend the A.dd line, and both are ready to add 17 to a

After the next step, the Main thread successfully adds 17 to the collection

At this time, the Thread is switched to thread-0 and still hangs in the line of A.dd (x), but there is already element 17 in set A, but thread-0 Thread will continue to add. After adding, there will be repeated element 17 in set A, which leads to bugs in the program

From the above example, it can be seen that the Suspend function of Idea Debug can be very convenient to simulate the problem of multithreaded competition in the process of debugging multithreaded programs, which is very convenient to write or Debug multithreaded programs

reference

  • Java Platform Debugger Architecture (JPDA) | Oracle Docs
  • Debugging Applications | Oracle Docs
  • Debug code – Help | IntelliJ IDEA

Original is not easy to reprint, please at the beginning of the famous article source and author. If my article is helpful to you, please click “like” to collect encouragement and support.