我注意到,当我使用条件断点进行调试时,执行速度会大大降低.我已经了解了一段时间,现在想了解原因.究竟发生了什么导致执行速度如此之慢?我知道正在添加条件,但如果我自己添加条件,我不会减慢执行速度.
例如,假设我们有以下代码.我们假设我们添加一个条件断点a=i.让我们将条件设置为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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我们自己编写条件.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
为什么这两者的运行时间如此显着不同?为什么第一个这么慢?
万一你想知道,我在Linux(Ubuntu)上使用Oracle-JDK-8.我用Eclipse和IntelliJ得到了相同的结果.
我在多个IDE上运行了第一个案例,看看是否存在差异.结果如下
的IntelliJ:
到达断点约9秒
完成约90秒(包括最初的9秒)
日食:
到达断点约9秒
完成约90秒(包括最初的9秒)
Netbeans的:
打破断点约12秒
完成约190秒(包括最初的12秒)
所以IntelliJ和Eclipse大致相同,但Netbeans要慢得多.
第二个例子几乎立即在所有IDE上运行,所以我没有做实验.(但我确实把这三个都运行了,看看是否有任何延迟,没有一个延迟.)
Ale*_*exR 19
我还没有实现IDE,调试器或JVM,所以我不能确定事情的进展与我在这里解释完全一样.
但.当代码与调试器一起运行时,JVM会解释代码,直到遇到断点.然后它停止并调用调试器(IDE).
JVM不支持条件断点,因此IDE使用"hack"来完成此功能.IDE只是添加一个普通断点.每次遇到断点时,IDE都会在警告用户之前评估表达式本身,如果评估结果为false,则会发送"continue"命令.
现在检查你的例子.在第二个示例中,JVM仅执行一次此类调用.在第一个例子中,这完成了100000次.每次JVM调用调试器并等待它解释条件并发送到JVM命令"continue"(就像在调试代码时手动执行一样).显然100000> 1,所以这个过程需要时间.
编辑:接下来的两个段落仅作为一个未经证明的假设.OP的实验表明它们是错误的.但是我不想完全删除它们:让我们将其解释为Eclipse团队的理论思考和改进建议.
关于IntelliJ vs Eclipse.同样,这只是假设.我看到IntelliJ使用条件断点工作得慢得多.我也知道IntelliJ中的条件断点不支持java编程语言的某些元素(例如匿名内部类).我可以得出结论,IntelliJ可能使用java之外的语言(例如groovy或其他东西)编译你编写的代码作为你的brekepoint的条件.这可能会导致性能下降.
我也知道eclipse不使用标准的javac编译器,但它自己的编译器有很多很酷的功能.我可以假设eclipase中的条件断点可能被编译为代码的一部分,即实际编译器会自动创建类似于示例编号2的代码.如果这是正确的,则此类代码的运行速度几乎与包含手动编写的if语句的代码一样快.
条件断点依赖于条件的解释(!)评估,只要达到断点的位置就可以完成.
命中断点很快:执行的顺序流程被中断,例如,通过用触发中断的某些代码替换该位置处的指令.但是条件的评估必须从内存中获取变量的值并计算结果,这与表达式在编译代码中评估的方式不同.预计会出现相当大的放缓.
虽然编译表达式导致机器指令(在Java中,至少在JIT编译之后),但解释表达式基于抽象语法树(数据结构),例如Equals( Variable( "i" ), Literal( 10000 ))和代表该数据结构的代码,取值("我")并计算操作("==").