Long-运行 方法中的调试性能较慢 Java
Slow Java Debugging Performance in Long-running Method
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.
问题
我最近调试了一个 class,它包含一个阻塞和长 运行 方法,用于执行大量操作。有趣的是,调试速度时常变化极大。我感觉速度与不同的调试方法相关(Step Into、Step Over、Step Return、Resume)。
用作实验的最小可重现示例
我写了一个最小的例子,可以在下面的帮助下重现和衡量我的感受 class:
测试class 只是测量计数器在"long running" 操作中递增的频率。为了检验这个假设,我在调用 longRunningOperation
之前设置了一个断点来停止程序,然后应用了不同的调试方法。
鉴于显示的断点,我使用不同的调试策略以不特定的顺序执行了几次重复测量(以尽量减少系统错误的可能性):
- 运行:简单执行程序(性能控制组)
- W/O 断点: 运行s 程序在调试模式下没有断点
- Resume:在断点处暂停,然后resume执行(IntelliJ F9)
- Over:在断点处暂停,然后跳过长运行方法(IntelliJ F8)
- Into+Return: 在断点处暂停,然后进入长运行方法和走出去 (IntelliJ F7, 然后SHIFT+F8)
- Into+Resume: 在断点处暂停,然后进入长运行方法和resume(IntelliJ F7,然后F9)
实验结果
| # | 运行 | W/O 断点 |简历 |结束 |进入+Return |进入+简历 |
|------------|------------|----------------|-------- ---|------------|------------|------------|
| 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 |
| | | | | | | |
|平均 | 865498895 | 862954025 | 868932231 | 13815636 | 13308900 | 870763334 |
|偏差 | 0,00% | 0,29% | -0,40% | 98,40% | 98,46% | -0,61% |
每个调试策略执行10次,System.out.println(res)
的结果显示在相应的列中。 Mean
行包含每个策略的 10 个测量值的平均值。 Deviation
行包含与 运行 策略的相对偏差。结果是用 IntelliJ IDEA 得到的,但在 Eclipse 中类似。
问题
结果表明调试时使用step over或step into + step out执行长运行方法与其他选项相比,慢 10 倍。但是我无法解释为什么会这样?调试器在内部做了什么来产生这种行为?
注意:我使用 Java 8 和 IntelliJ IDEA 2016.2 在 Windows 10 上执行了测量。
为了在您的机器上重现该行为,我已将小 class 放入 Gist。
我也观察到了这一点。在 eclipse 中一定是一个糟糕的 step over 实现。我通常在一个复杂的方法之后设置断点并使用 resume 跳转到那里而不是使用 step over 因为经常 resume 快多了。
因为 step over 可以在内部编程以执行与我设置断点并按 resume 时相同的操作,这可能刚刚实现以一种糟糕的方式。
edit: 才意识到这不一样。当您按下 resume 并跳转到断点时,您可能会在其他线程触发的断点处停止。
我仍然遇到同样的问题,我相信与其说是实现,不如说是 Java 一般调试(如果是实现的错,IntelliJ 必须同样糟糕地实现它,并且两个 IDE 都实现这对我来说似乎不太可能)。
我在 SO 上发现 this post 这让我怀疑在跨过代码时,Java 永远不会一步一步离开未优化的解释字节码。当继续时,我相信它会检查到它可能 运行 并在优化方法中这样做,使它更快。
但是请注意,这只是我个人的猜测,我没有任何事实可以证明这一点(除了那些操作的性能)。
但是我确实找到了这个问题的 "solution"。即:Run to Line
.
此功能将 运行 代码直至光标当前所在的行。因此,它似乎与在行中添加断点,继续执行,然后再次删除断点具有相同的效果。使用这种方法,可以通过将光标放在下一行并点击 Ctrl+R
(默认快捷方式)来 "step over" 一行。
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.
问题
我最近调试了一个 class,它包含一个阻塞和长 运行 方法,用于执行大量操作。有趣的是,调试速度时常变化极大。我感觉速度与不同的调试方法相关(Step Into、Step Over、Step Return、Resume)。
用作实验的最小可重现示例
我写了一个最小的例子,可以在下面的帮助下重现和衡量我的感受 class:
测试class 只是测量计数器在"long running" 操作中递增的频率。为了检验这个假设,我在调用 longRunningOperation
之前设置了一个断点来停止程序,然后应用了不同的调试方法。
鉴于显示的断点,我使用不同的调试策略以不特定的顺序执行了几次重复测量(以尽量减少系统错误的可能性):
- 运行:简单执行程序(性能控制组)
- W/O 断点: 运行s 程序在调试模式下没有断点
- Resume:在断点处暂停,然后resume执行(IntelliJ F9)
- Over:在断点处暂停,然后跳过长运行方法(IntelliJ F8)
- Into+Return: 在断点处暂停,然后进入长运行方法和走出去 (IntelliJ F7, 然后SHIFT+F8)
- Into+Resume: 在断点处暂停,然后进入长运行方法和resume(IntelliJ F7,然后F9)
实验结果
| # | 运行 | W/O 断点 |简历 |结束 |进入+Return |进入+简历 | |------------|------------|----------------|-------- ---|------------|------------|------------| | 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 | | | | | | | | | |平均 | 865498895 | 862954025 | 868932231 | 13815636 | 13308900 | 870763334 | |偏差 | 0,00% | 0,29% | -0,40% | 98,40% | 98,46% | -0,61% |
每个调试策略执行10次,System.out.println(res)
的结果显示在相应的列中。 Mean
行包含每个策略的 10 个测量值的平均值。 Deviation
行包含与 运行 策略的相对偏差。结果是用 IntelliJ IDEA 得到的,但在 Eclipse 中类似。
问题
结果表明调试时使用step over或step into + step out执行长运行方法与其他选项相比,慢 10 倍。但是我无法解释为什么会这样?调试器在内部做了什么来产生这种行为?
注意:我使用 Java 8 和 IntelliJ IDEA 2016.2 在 Windows 10 上执行了测量。
为了在您的机器上重现该行为,我已将小 class 放入 Gist。
我也观察到了这一点。在 eclipse 中一定是一个糟糕的 step over 实现。我通常在一个复杂的方法之后设置断点并使用 resume 跳转到那里而不是使用 step over 因为经常 resume 快多了。
因为 step over 可以在内部编程以执行与我设置断点并按 resume 时相同的操作,这可能刚刚实现以一种糟糕的方式。
edit: 才意识到这不一样。当您按下 resume 并跳转到断点时,您可能会在其他线程触发的断点处停止。
我仍然遇到同样的问题,我相信与其说是实现,不如说是 Java 一般调试(如果是实现的错,IntelliJ 必须同样糟糕地实现它,并且两个 IDE 都实现这对我来说似乎不太可能)。
我在 SO 上发现 this post 这让我怀疑在跨过代码时,Java 永远不会一步一步离开未优化的解释字节码。当继续时,我相信它会检查到它可能 运行 并在优化方法中这样做,使它更快。
但是请注意,这只是我个人的猜测,我没有任何事实可以证明这一点(除了那些操作的性能)。
但是我确实找到了这个问题的 "solution"。即:Run to Line
.
此功能将 运行 代码直至光标当前所在的行。因此,它似乎与在行中添加断点,继续执行,然后再次删除断点具有相同的效果。使用这种方法,可以通过将光标放在下一行并点击 Ctrl+R
(默认快捷方式)来 "step over" 一行。