Java方法似乎花费了我无法解释的大量时间



使用JProfiler,我在Java代码中发现了一个我无法理解的热点。JProfiler解释说,该方法平均花费150 μ s(不进行预热时为674 μ s),这还不包括调用后代方法所需的时间。150μs可能看起来不多,但在这个应用程序中,它加起来(我的用户经历过),而且与其他方法相比,它似乎很多,对我来说似乎比这个更复杂。因此这对我很重要。

private boolean assertReadAuthorizationForFields(Object entity, Object[] state,
        String[] propertyNames) {
    boolean changed = false;
    final List<Field> fields = FieldUtil.getAppropriatePropertyFields(entity, propertyNames);
    // average of 14 fields to iterate over
    for (final Field field : fields) {
        // manager.getAuthorization returns an enum type
        // manager is a field referencing another component
        if (manager.getAuthorization(READ, field).isDenied()) {
            FieldUtil.resetField(field.getName(), state, propertyNames);
            changed = true;
        }
    }
    return changed;
}

我自己在不同的方向上最小化了这个方法,但它从来没有教会我很多有用的东西。我必须强调,jprofiler报告的持续时间(150 mu;s)仅仅是关于这个方法中的代码,不包括执行getAuthorizationisDeniedresetField等所需的时间。这也是为什么我开始只是发布这个片段,没有太多的上下文,因为问题似乎是与这段代码,而不是它的后续后代方法调用。

也许你会争论为什么& & &;如果你觉得我在看鬼:)不管怎样,谢谢你的时间!

可能会减慢速度的候选行为:

  • 主要效果:明显迭代。如果你有很多领域……你说平均14,这是相当重要的
  • 主要影响:热点内联意味着被调用的方法包含在你的时间内——这可能很明显,因为你的方法调用使用反射。getAppropriatePropertyFields对类字段定义元数据进行自省;resetField动态调用setter方法(可能使用Method.invoke()??)。如果你迫切需要性能,你可以通过HashSet(映射ElementClass->FieldMetadataAndMethodHandle)使用缓存,这可以包含字段元数据和setter方法的MethodHandles(而不是使用method)。调用,这是缓慢的)。然后,您将仅在应用程序启动期间进行反射,并将使用JVM的快速动态调用支持。
  • 影响较小-但乘以迭代次数:如果你有非常大的状态和属性名称数组,并且它们使用原始字段,那么它们将在方法调用期间涉及某种程度的复制(方法参数传递-'value'实际上意味着通过引用传递/通过原始复制传递)

我建议您自己对方法进行计时,因为分析器并不总是给出准确的计时。

用下面的代码创建一个微基准测试,并运行至少2秒。要计算方法调用有多大的不同,请注释它们并硬编码它们返回的值。

我认为问题是FieldUtil正在使用反射,不缓存它使用的字段。

最新更新