6

Update: While I asked this question over a year ago, there is still no answer. Out of curiosity, I reproduced the problem I observed with Eclipse today with IntelliJ IDEA.

Problem

I was recently debugging a class that contained a blocking and long-running method doing lots of operations. Interestingly the debugging speed varied extremely from time to time. I had a feeling that the speed correlated with the different debugging methods (Step Into, Step Over, Step Return, Resume).

Minimal reproducible example used as experiment

I wrote a minimal example and could reproduce and measure my feeling with the help of the following class:

java debugging performance testing

The test class simply measures how often the counter is incremented in the "long running" operation. To test the hypothesis I set a breakpoint to halt the program before invoking the longRunningOperation and then applied the different debugging methods.

Given the shown breakpoint, I performed several repeated measurements in no particular order (to minimize chance of a systematic error) with different debugging strategies:

  1. Run: simply execute the program (performance control group)
  2. W/O Breakpoint: Runs the program in debug mode without a breakpoint
  3. Resume: Halt at the breakpoint, then resume the execution (IntelliJ F9)
  4. Over: Halt at the breakpoint, then step over the long running method (IntelliJ F8)
  5. Into+Return: Halt at the breakpoint, then step into the long running method and step out (IntelliJ F7, then SHIFT+F8)
  6. Into+Resume: Halt at the breakpoint, then step into the long running method and resume (IntelliJ F7, then F9)

Experimental results

| #         | Run       | W/O Breakpoint | Resume    | Over     | Into+Return | Into+Resume |
|-----------|-----------|----------------|-----------|----------|-------------|-------------|
| 1         | 863342711 | 862587196      | 872204399 | 14722473 | 12550871    | 870687830   |
| 2         | 868929379 | 864245840      | 872166407 | 14139145 | 12487883    | 870626416   |
| 3         | 865544040 | 852645848      | 872988659 | 14352193 | 12459235    | 871062770   |
| 4         | 868100763 | 863198685      | 867518560 | 12261625 | 14696307    | 871365658   |
| 5         | 865157647 | 866257267      | 862671156 | 12524087 | 14620150    | 868541690   |
| 6         | 865348827 | 863449576      | 864416490 | 14410005 | 14592026    | 868784314   |
| 7         | 866957323 | 865379147      | 873324542 | 14326951 | 12648924    | 868621635   |
| 8         | 860129057 | 868993541      | 867785706 | 14434965 | 14380032    | 875011465   |
| 9         | 865961737 | 857872085      | 871137322 | 12440011 | 12262172    | 871357411   |
| 10        | 865517465 | 864911063      | 865109071 | 14544906 | 12391397    | 871574154   |
|           |           |                |           |          |             |             |
| Mean      | 865498895 | 862954025      | 868932231 | 13815636 | 13308900    | 870763334   |
| Deviation | 0,00%     | 0,29%          | -0,40%    | 98,40%   | 98,46%      | -0,61%      |

Each debugging strategy was executed 10 times and the result of System.out.println(res) is shown in the respective columns. The Mean row contains the mean value of the 10 measurements for each strategy. The Deviation row contains the relative deviation to the Run strategy. The results are obtained with IntelliJ IDEA, but are similar in Eclipse.

Question

The results indicate that using step over or step into + step out to execute a long running method during debugging is more than 10x slower compared to the other options. However I cannot explain why this happens? What does the debugger internally do to yield such a behavior?

Note: I executed the measurements on Windows 10 with Java 8 and IntelliJ IDEA 2016.2.


To reproduce the behavior on your machine, I have put the small class into a Gist.

Fabian Kleiser
  • 2,988
  • 3
  • 27
  • 44

2 Answers2

4

I am still experiencing the same problem and I believe it is not so much the implementation as Java debugging in general (If it was the implementation's fault IntelliJ had to implement it equally badly and that both IDEs implement it that badly seems rather unlikely to me).

I found this post here on SO that led me to the suspicion that when stepping over code, Java never leaves the unoptimized interpret bytecode step by step. When continuing I believe it checks until which point it may run and does so in the optimized approach, making this a hell lot faster.
Note however that this is only my personal suspicion and that I do not have any facts that would proof this (other than the performance of those operations).

I did however find a "solution" to this problem. Namely: Run to Line.
This feature will run the code up to the line in which your cursor is currently placed. Therefore it seems to have the very same effect as adding a breakpoint to the line, continuing the execution and then removing the breakpoint again. With this approach one can "step over" a line by placing the cursor on the next line and hitting Ctrl+R (default shortcut for this).

Raven
  • 2,951
  • 2
  • 26
  • 42
1

I also observed this. Must be a bad step over implementation in eclipse. I usually set breakpoints after a complex method and jump there with resume instead of using step over because often resume is A LOT faster.

As step over could be programmed internally to do the same as when I set a breakpoint and press resume this is probably just implemented in a bad way.

edit: just realised it's not the same. When you press resume and jump to a breakpoint you might stop at a breakpoint triggered by an other thread.

tObi
  • 1,864
  • 3
  • 20
  • 32