2.4 的性能似乎有所下降。SampleActuatorApplication
对我来说,运行时间多了 0.5 秒。
[spring-projects/spring-boot]SampleActuatorApplication 在 2.4 中运行速度较慢
回答
CachedIntrospectionResults.getBeanInfo
可能是一件值得关注的事情。或者现在似乎更频繁地触发它的任何东西。但这可能是 Spring-Framework 中的回归,而不是 Boot 中的回归。
2.4.0. 快照 2.3.2.发布
目前我的时间有限,否则我会自愿修复此问题或至少进一步调查。
不用担心,仅此分析就非常有帮助!
@philwebb 顺便说一句,你在比较哪些版本?我目前正在比较 2.3.2 与 2.4.0-SNAPSHOT,并不能真正看到 500 毫秒的差异。也许 100 毫秒——如果有的话。您使用哪个 JDK?我想尽可能接近你的设置 - 也许我可以提供至少一些关于这个的更多见解......
根据 #23147 中的讨论,设置-Dspring.beaninfo.ignore=false
可能是值得尝试的事情(即使没有预料到会产生这种影响)。
:( 我认为我们最初将其设置为true
尝试提高性能。
这仍然需要一些适当的测试——我还没有走得太远。但单独进行基准测试Introspector.getBeanInfo
会产生以下结果。
Benchmark Mode Cnt Score Error Units
MyBenchmark.beanInfoWithIgnore avgt 10 72207,788 ± 7550,716 ns/op
MyBenchmark.beanInfoWithIgnore:·gc.alloc.rate avgt 10 677,887 ± 65,626 MB/sec
MyBenchmark.beanInfoWithIgnore:·gc.alloc.rate.norm avgt 10 63960,015 ± 293,228 B/op
MyBenchmark.beanInfoWithIgnore:·gc.churn.G1_Eden_Space avgt 10 679,296 ± 94,402 MB/sec
MyBenchmark.beanInfoWithIgnore:·gc.churn.G1_Eden_Space.norm avgt 10 64132,960 ± 7085,231 B/op
MyBenchmark.beanInfoWithIgnore:·gc.churn.G1_Old_Gen avgt 10 0,008 ± 0,006 MB/sec
MyBenchmark.beanInfoWithIgnore:·gc.churn.G1_Old_Gen.norm avgt 10 0,737 ± 0,534 B/op
MyBenchmark.beanInfoWithIgnore:·gc.count avgt 10 56,000 counts
MyBenchmark.beanInfoWithIgnore:·gc.time avgt 10 26,000 ms
MyBenchmark.beanInfoWithoutIgnore avgt 10 13,241 ± 1,495 ns/op
MyBenchmark.beanInfoWithoutIgnore:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec
MyBenchmark.beanInfoWithoutIgnore:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁶ B/op
MyBenchmark.beanInfoWithoutIgnore:·gc.count avgt 10 ≈ 0 counts
显然,这并不能以 1:1 的方式转化为每个应用程序 - 事实上,它很大程度上取决于类加载器设置等 - 但可能是应对当前性能下降的一个选项。我很感兴趣这是否会对您的机器产生影响@philwebb
好吧,暂时把 beaninfo 的东西放在一边,我现在可以在运行 fat jar 时重现这一点,甚至可以看到从 500 毫秒到 1 秒不等的差异。分析还产生了一些其他非常有趣的结果(顶部 2.4.0.SNAPSHOT 与底部 2.3.2.RELEASE):
当放大 2.4.0 分析时,这再次证实了对可能由于触发更多调用而导致性能下降的初步分析Introspector.getBeanInfo/CachedIntrospectionResults
:
不幸的是,从 Spring-Framework 5.1 开始,似乎触发大部分内容的代码已经被弃用:
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
// This seems to trigger most of the expensive usages of CachedIntrospectionResults
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// postProcessPropertyValues() is deprecated as of 5.1 and makes expensive filtering above obsolete
pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
也许这是@jhoeller 的这个更改https://github.com/spring-projects/spring-framework/commit/a3c5625d4e1e2b5ca720b8e74797ee8bd30f54e4 ?这是相当新的。
我不明白所提到的更改实际上应该如何(负面)影响这一点 - 考虑到它只是改变了 BeanPostProcessors 的迭代方式。事实上,我建议这样做是为了节省一些周期,以便在不相关的处理器上进行不必要的迭代……但也许有一个我还没有看到的奇怪的副作用。
我认为回归是通过https://github.com/spring-projects/spring-framework/commit/7207f7645c7d48392d8d7518a9da305b2090c12d引入的。的更改AbstractAutoProxyCreator
导致默认实现postProcessProperties
返回null
,而之前返回的是 (empty) PropertyValues
。再说一次 - 如果我们删除已弃用的调用,恕我直言,postProcessPropertyValues
就根本不需要昂贵的过滤。
很好,@dreis2211 - 我将AbstractAutoProxyCreator
使用快捷方式返回值恢复重新声明的方法。不幸的是,这种微妙之处被忽视了,无论如何,我认为这些方法与界面中的默认方法相同。
postProcessPropertyValues
删除已弃用的@jhoeller怎么样?如果你问我的话,这会解决很多微妙的问题......
这是一个非常古老的回调(广泛用于依赖注入目的),所以我宁愿暂时保持它完整并在 6.0 中删除它。不过,是的,移除后会更好。
很公平...