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.
问题
我最近正在调试一个类,该类包含一个执行大量操作的阻塞且长时间运行的方法。有趣的是,调试速度变化很大不时。我感觉速度与不同的调试方法相关(Step Into、Step Over、Step Return、Resume)。
用作实验的最小可重现示例
我写了一个最小的例子,可以在下面的类的帮助下重现和衡量我的感受:
测试类只是测量计数器在“长时间运行”操作中递增的频率。为了检验这个假设,我设置了一个断点以在调用 longRunningOperation
之前暂停程序,然后应用不同的调试方法。
鉴于显示的断点,我使用不同的调试策略以不特定的顺序执行了几次重复测量(以尽量减少系统错误的可能性):
- Run:简单的执行程序(性能控制组)
- W/O Breakpoint:在没有断点的 Debug模式下运行程序
- 恢复:在断点处暂停,然后恢复执行(IntelliJ F9)
- Over:在断点处暂停,然后越过长时间运行的方法 (IntelliJ F8)
- Into+Return:在断点处暂停,然后进入长时间运行的方法并退出(IntelliJ F7,然后 SHIFT+F8)
- Into+Resume:在断点处暂停,然后进入长时间运行的方法并恢复(IntelliJ F7,然后 F9)
实验结果
| # | 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% |
每个调试策略执行 10 次,System.out.println(res)
的结果显示在相应的列中。 Mean
行包含每个策略的 10 个测量值的平均值。 Deviation
行包含与Run 策略的相对偏差。结果是使用 IntelliJ IDEA 获得的,但在 Eclipse 中类似。
问题
结果表明,在调试期间使用step over 或step into + step out 执行长时间运行的方法比慢10 倍与其他选项相比。但是我无法解释为什么会这样?调试器在内部做了什么来产生这种行为?
注意:我使用 Java 8 和 IntelliJ IDEA 2016.2 在 Windows 10 上执行了测量。
为了在您的机器上重现该行为,我已将小类放入 Gist 中.
最佳答案
我仍然遇到同样的问题,我相信它不是一般的 Java 调试那样的实现(如果是实现的错误,IntelliJ 必须同样糟糕地实现它并且两个 IDE 都实现它似乎不太可能对我来说)。
我找到了 this post在这里,这让我怀疑在单步执行代码时,Java 永远不会一步一步地离开未优化的解释字节码。当继续时,我相信它会检查它可以运行到哪个点,并以优化的方法执行此操作,从而使它更快。
但请注意,这只是我个人的猜测,我没有任何事实可以证明这一点(除了那些操作的性能)。
不过,我确实找到了这个问题的“解决方案”。即:Run to Line
.
此功能会将代码运行到光标当前所在的行。因此,它似乎与在行中添加断点,继续执行,然后再次删除断点具有相同的效果。使用这种方法,可以通过将光标放在下一行并按 Ctrl+R
(默认快捷方式)来“跳过”一行。
关于java - 长时间运行的方法中的 Java 调试性能较慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28807452/