为什么条件调试这么慢?

Nic*_*ich 26 java debugging

我注意到,当我使用条件断点进行调试时,执行速度会大大降低.我已经了解了一段时间,现在想了解原因.究竟发生了什么导致执行速度如此之慢?我知道正在添加条件,但如果我自己添加条件,我不会减慢执行速度.

例如,假设我们有以下代码.我们假设我们添加一个条件断点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语句的代码一样快.


lau*_*une 8

条件断点依赖于条件的解释(!)评估,只要达到断点的位置就可以完成.

命中断点很快:执行的顺序流程被中断,例如,通过用触发中断的某些代码替换该位置处的指令.但是条件的评估必须从内存中获取变量的值并计算结果,这与表达式在编译代码中评估的方式不同.预计会出现相当大的放缓.

虽然编译表达式导致机器指令(在Java中,至少在JIT编译之后),但解释表达式基于抽象语法树(数据结构),例如Equals( Variable( "i" ), Literal( 10000 ))和代表该数据结构的代码,取值("我")并计算操作("==").

  • "不是以同样的方式完成的".有什么不同吗?你能解释一下吗? (2认同)
  • 请理解我的答案是试图逃避技术细节,这将使其极端膨胀.通过搜索解释器,抽象语法树等可以获得进一步的读取.详细信息在调试器之间变化,例如Java IDE中的内容或C/C++的gdb等. (2认同)