为什么VisualVM Sampler不提供有关CPU负载的完整信息(方法时间执行)?

时间:2011-11-15 01:01:39

标签: profiler visualvm

问题是:VisualVM采样器按时间显示调用树。对于某些方法,采样器仅显示“自我时间”,因此我看不出是什么使这种方法变慢。 Here is an example
如何增加剖析的深度?

2 个答案:

答案 0 :(得分:44)

不幸的是,由于多种原因,采样分析器在进行深入分析时相当有限:

  • 采样器受采样周期的限制:例如,VisualVM目前的最小采样周期为20ms。现代处理器在那段时间内可以执行数百万条指令 - 当然只需要调用几种简短的方法并从中返回。

    虽然一个明显的解决方案是减少采样周期,但这也会增加探查器对您的应用程序的影响,提供uncertainty principle的一个很好的例子。

  • 采样器容易被内联代码混淆: JVM和任何体面的编译器都会内联琐碎和/或频繁调用的方法,从而将代码合并到调用者的代码中。采样分析器无法分辨每种方法的哪些部分实际属于它,哪些部分属于内联调用。

    对于VisualVM 自我时间实际上包括方法任何内联代码的执行时间。

  • 采样器可能会被高级VM弄糊涂:例如,在现代JVM实现中,方法没有稳定的表示。想象一下例如以下方法:

    void A() {
        ...
        B();
        ...
    }
    

    当JVM启动B()时,直接从字节码解释,因此需要花费相当多的时间才能使采样器看到它。然后,在一段时间后,JVM决定B()是优化的良好候选者,并将其编译为本机代码,从而使其更快。又过了一段时间,JVM可能会决定内联B()的调用,将其代码合并到A()

    最好的情况是,采样分析器将显示首次运行的成本,然后任何后续运行的成本将包含在调用者花费的时间内。不幸的是,这可能会让没有经验的开发人员误解低估内联方法的成本。

    最糟糕的是,该费用可能会分配给兄弟电话,而不是来电者。例如,我目前正在使用VisualVM分析应用程序,其中热点似乎ArrayList.size()方法。在我的Java实现中,该方法是一个简单的字段getter,任何JVM都应该快速内联。然而,个人资料显示它是一个主要的时间消费者,完全忽略了一堆明显要贵得多的HashMap电话。

避免这些弱点的唯一方法是使用仪器分析器,而不是采样分析器。检测分析器(例如VisualVM中 Profiler 选项卡提供的分析器)实质上记录了所选代码中的每个方法入口和出口。不幸的是,检测分析器对分析代码的影响相当大:

  • 他们在每个方法周围插入监控代码,这完全改变了JVM处理方法的方式。由于额外的代码,甚至简单的字段getter / setter方法也不会被内联,从而扭曲任何结果。分析器通常会尝试考虑这些变化,但并不总是成功。

  • 它们导致大量减速到配置文件代码,这使得它们完全不适合监视完整的应用程序。

由于这些原因,仪器分析器主要适用于分析已使用其他方法(例如采样分析器)检测到的热点。通过仅检测一组选定的类和/或方法,可以将分析副作用限制为应用程序的特定部分。

答案 1 :(得分:1)

示例中没有任何错误。看起来updateInfoInDirection()调用了new SequenceInfo()SequenceInfo.next()。 “自我时间”意味着时间花费在方法本身的代码中(方法updateInfoInDirection()在获取线程样本时位于堆栈的底部)。