41

I have noticed that when I debug with conditional breakpoints, execution is dramatically slowed down. I have known this for a while, and would now like to understand why. What exactly is happening that causes execution to be so slow? I know that a conditional is being added, but if I add the conditional myself, I don't slow down execution.

For example, lets say we have the following code. And lets say we add a conditional breakpoint a=i. Lets just set the conditional to i==10000.

public class Main {

    public static void main(String[] args) {
        int a = 0;

        for (int i = 0; i<100000; i++) {
                a = i;  //put breakpoint here (if i == 10000)
        }
        System.out.println("Done! a=" + a);
    }
}

Now lets instead, write the conditional ourselves.

public class Main {

    public static void main(String[] args) {
        int a = 0;

        for (int i = 0; i<100000; i++) {
            if (i == 10000)
                a = i; //put a NON-conditional breakpoint here
            else a = i;
        }
        System.out.println("Done! a=" + a);
    }
}

Why is the run time of both of these so dramatically different? Why is the first one so much slower?

In case your wondering, I am using Oracle-JDK-8 on Linux (Ubuntu). I get the same results with Eclipse and IntelliJ.


Experiment results

I ran the first case on multiple IDE's to see if there is a difference. Here are the results

IntelliJ:

~9 seconds to hit breakpoint

~90 seconds to hit completion (including initial 9 seconds)

Eclipse:

~9 seconds to hit breakpoint

~90 seconds to hit completion (including initial 9 seconds)

Netbeans:

~ 12 seconds to hit breakpoint

~ 190 seconds to hit completion (including initial 12 seconds)

So IntelliJ and Eclipse are about the same, but Netbeans is much much slower.

The second example runs almost instantaneously on all IDE's, so I did not do an experiment. (But I did run it all all three to see if any of them had a delay, none of them did.)

11
  • 4
    While I dont know for sure I would imagine its got something to do with how the debugger receives and evaluates data from the target JVM. I imagine its very similar to the way JConsole and VisualVM gather this data using RMI. Thats definitely not even in the same league as having a conditional in the source code, which makes them very hard to compare, because they are doing entirely different things. Commented Jul 9, 2014 at 18:21
  • What IDE are you using? Is it probably IntelliJ? Commented Jul 9, 2014 at 18:21
  • @AlexR yes IntelliJ, but I've had the same problem in eclipse. Commented Jul 9, 2014 at 18:23
  • What tool(s) are you using for debugging? Commented Jul 9, 2014 at 18:24
  • 2
    My best guess is a non-conditional breakpoint, the debugger stops the code without question while with a condition, that condition must be evaluated before the decision to stop or continue is made. As @MarkW said, getting the data is what takes some time as, to evaluate the condition, the debugger stops at the breakpoint, get the data and evaluate it. Commented Jul 9, 2014 at 18:24

2 Answers 2

25

I have not implemented IDE, debugger or JVM, so I can not be sure that the things are going exactly as I will explain here.

But. When code is running with debugger the JVM interprets the code until it meets breakpoint. Then it stops and calls debugger (IDE).

JVM's do not support conditional breakpoints, so IDE's use a "hack" to accomplish this feature. The IDE simply adds a normal breakpoint. Every time a breakpoint is hit, the IDE evaluates the expression itself before alerting the user, if the evaluation is false, it sends the "continue" command.

Now examine your examples. In second example JVM performs such call only once. In first example this is done 100000 times. Each time JVM calls debugger and waits until it interprets the condition and sends to JVM command "continue" (exactly as you can do manually when you are debugging your code). Obviously 100000>1, so this process takes time.

EDIT: the next 2 paragraphs were written as an not proven assumption only. The experiments of OP showed that they are wrong. I however do not want to remove them completely: let's interpret this as a theoretical thinking and improvement proposal for Eclipse team.

Concerning IntelliJ vs Eclipse. Again, this is assumption only. I saw that IntelliJ works much slower with conditional breakpoints. I know also that conditional breakpoints in IntelliJ do not support some elements of java programming language (e.g. anonymous inner classes). I can conclude that IntelliJ probably compiles the code that you write as a condition of your brekepoint using language other than java (for example groovy or something). This probably causes additional performance degradation.

I also know that eclipse does not use standard javac compiler but its own compiler that has a lot of cool features. I can assume that probably conditional breakpoints in eclipase are compiled as a part of your code, i.e. actually compiler automatically creates code like your example number 2. If this is correct such code will run almost as quickly as code that contains manually written if statement.

Sign up to request clarification or add additional context in comments.

10 Comments

Im curious now.. Im about to run this exact sample in eclipse.
Try it. I will be glad to know the results.
That is very neat, what you said about Eclipse. Will be surprising to know if it is true. What about NetBeans?
A good comparison can be made if the debugger has a feature of executing a canned command sequence whenever a breakpoint is hit. Measure the time for a breakpoint with the single command "continue" against the one for a breakpoint that evaluates a condition and executes the same "continue".
@ADTC, I aware about Java decompiler. I also fluently read java byte code directly. However the condition injection is not necessarily compiled statically into .class file. It can be injected during file loading e.g. by agent.
|
11

Conditional breakpoints rely on the interpretative (!) evaluation of the condition, to be done whenever the location of the breakpoint is hit.

Hitting a breakpoint is fast: the sequential flow of execution is interrupted, e.g., by replacing the instruction at that location by some code that triggers an interrupt. But the evaluation of the condition must fetch the values of the variables from memory and compute the result, which isn't done the same way the expression would evaluate in compiled code. A considerable slow-down is to be expected.

While a compiled expression results in machine instructions (in Java, at least after JIT compilation), an interpreted expression is based on an abstract syntax tree (a data structure), such as Equals( Variable( "i" ), Literal( 10000 )) and code that descends on that data structure, fetches values ("i") and computes operations ("==").

2 Comments

"isn't done in the same way". What is different? Could you expound upon that.
Please understand that my answer is trying to evade technical details which would bloat it in the extreme. Further reading can be obtained by googling for interpreters, abstract syntax tree, etc. Details vary between debuggers such as what you have in a Java IDE or gdb for C/C++ and others.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.