2011-11-15 87 views

回答

1

是没有错的例子。它看起来像updateInfoInDirection()调用new SequenceInfo()SequenceInfo.next()。 '自我时间'意味着时间花费在方法本身的代码中(方法updateInfoInDirection()在采样线程时位于堆栈底部)。

43

不幸的是采样廓当它归结为深入分析是相当有限的,由于多种原因:

  • 采样由采样周期的限制:例如,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方法也可能因为额外的代码而不再内联,从而导致任何结果偏差。分析人员通常试图解释这些变化,但并不总是成功的。

  • 他们造成巨大的起伏的轮廓代码,这使得它们完全不适合用于监控完整的应用程序。

由于这些原因插装分析器大多适合于分析已经被使用另一种方法,例如一个取样分析器检测的热点。通过仅检测选定的一组类和/或方法,可以将分析副作用限制到应用程序的特定部分。

相关问题