为什么Java CPU概要文件(使用visualvm)显示了对一个什么都不做的方法如此多的点击?

为什么Java CPU概要文件(使用visualvm)显示了对一个什么都不做的方法如此多的点击?,java,performance,visualvm,Java,Performance,Visualvm,这是我以前在其他环境中使用其他分析工具时看到的,但在这种情况下尤其引人注目 我对一个运行了大约12分钟的任务做了一个CPU配置文件,它显示了几乎一半的时间花在一个实际上什么都不做的方法上:它有一个空的主体。这是什么原因造成的?我不相信这个方法被调用的次数是荒谬的,当然也不会占执行时间的一半 值得一提的是,该方法被称为startContent(),用于通知解析事件。事件被传递到一个过滤器链(可能有十几个),每个过滤器上的startContent()方法除了调用链中下一个过滤器上的startCont

这是我以前在其他环境中使用其他分析工具时看到的,但在这种情况下尤其引人注目

我对一个运行了大约12分钟的任务做了一个CPU配置文件,它显示了几乎一半的时间花在一个实际上什么都不做的方法上:它有一个空的主体。这是什么原因造成的?我不相信这个方法被调用的次数是荒谬的,当然也不会占执行时间的一半

值得一提的是,该方法被称为startContent(),用于通知解析事件。事件被传递到一个过滤器链(可能有十几个),每个过滤器上的startContent()方法除了调用链中下一个过滤器上的startContent()外,几乎什么都不做

这是纯Java代码,我正在Mac上运行它

随附的是CPU采样器输出的屏幕截图:

下面是一个显示调用堆栈的示例:

(由于假期而延迟)以下是两张显示分析器输出的图片。这些数字远比我期望的个人资料要多。分析器的输出似乎完全有意义,而采样器的输出是虚假的

正如你们中的一些人所猜测的,所讨论的工作是运行Saxon XML模式验证器(在9Gb的输入文件上)。该概要文件显示,大约一半的时间用于根据简单类型验证元素内容(发生在endElement处理期间),大约一半的时间用于测试关键约束的唯一性;这两个探查器视图突出显示了任务这两个方面所涉及的活动


我无法提供来自客户端的数据。

我没有使用VisualVM,但我怀疑问题可能是因为这种空方法的检测开销

如果将方法调用记录类型设置为动态检测,则检测分析类的所有方法。这会产生一些开销,这对于执行时间非常短的方法来说非常重要。如果这些方法被频繁调用,那么这些方法的测量时间将非常长。此外,由于插装,热点编译器可能无法对其进行优化。在极端情况下,这种方法成为主要的热点,尽管对于非仪器运行来说并非如此。例如,XML解析器读取下一个字符的方法。此方法返回速度非常快,但可能在短时间内被调用数百万次

基本上,探查器会添加自己的“时间长度检测代码”,但在一个空方法中,探查器会将所有时间都花在这一点上,而不是实际允许该方法运行


如果可能的话,我建议告诉VisualVM停止检测该线程,如果它支持这样的过滤。

一般认为,使用探查器(用于发现性能问题,而不是测量问题)比任何其他方法都要好得多,确实比简单的随机暂停方法要好得多

这一假设只是常识——它没有理论或实践依据。 有很多关于评测的学术同行评议论文,但我读过的文章中没有一篇能够说明这一点,更不用说证实这一点了。 这是学术界的一个盲点,不是一个大盲点,但它确实存在

现在谈谈你的问题-

在显示调用堆栈的屏幕截图中,这就是所谓的“热路径”,约占线程内CPU时间的60%。假设名称中带有“saxon”的代码是您感兴趣的,那么它是:

net.sf.saxon.event.ReceivingContentHandler.startElement
net.sf.saxon.event.ProxyReceiver.startContent
net.sf.saxon.event.ProxyReceiver.startContent
net.sf.saxon.event.StartTagBuffer.startContent
net.sf.saxon.event.ProxyReceiver.startContent
com.saxonica.ee.validate.ValidationStack.startContent
com.saxonica.ee.validate.AttributeValidator.startContent
net.sf.saxon.event.TeeOutputter.startContent
net.sf.saxon.event.ProxyReceiver.startContent
net.sf.saxon.event.ProxyReceiver.startContent
net.sf.saxon.event.Sink.startContent
首先,在我看来,它必须进行I/O,或者至少等待其他进程为其提供内容。如果是这样的话,你应该看墙上的时钟时间,而不是CPU时间

第二,问题可能出现在函数调用下面函数的任何调用站点。如果任何这样的调用不是真正必要的,并且可以跳过或不太频繁地执行,那么它将大大减少时间。 我的怀疑被吸引到
开始缓冲
验证
,但你最清楚

我还可以提出其他观点,但这些是主要的观点

在编辑问题后添加。 我倾向于假设您正在寻找优化代码的方法,而不仅仅是为了获得数字

它看起来仍然只是CPU时间,而不是挂钟时间,因为热路径中没有I/O。在您的情况下,这可能没问题,但它的意思是,在12分钟的挂钟时间中,11分钟可以用于I/O等待,1分钟用于CPU。如果是这样的话,你可以在CPU部分减少30秒的脂肪,只需将时间缩短30秒。 这就是为什么我更喜欢在墙上时钟时间采样,所以我有整体的观点

如果只看热点路径,你就不会得到一个真实的画面。 例如,如果热路径表示函数F在热路径上的时间占40%,则仅表示F的成本不低于40%。它可能会更多,因为它可能在其他不那么热的路径上。所以你可以有一个很好的机会来加快速度,但是在剖析器选择给你展示的特定路径上,它没有得到太多的曝光,所以你没有给予太多的关注。 事实上,一个大的时间占用者可能根本不会出现,因为在任何特定的热路径上总是有一些更大的东西,比如
new
,或者因为它有多个名称,比如模板化的集合类构造函数

它不会显示任何行分辨率信息。 如果您想检查一个假定的高成本例程以了解成本的原因,您必须查看其中的行。在查看例程时,有一种倾向是说“它只是在做它应该做的事情”,但是如果您查看的是规范