We have observed a performance regression (~9–10 seconds delay) during the Spring context refresh phase after upgrading our application from Spring 6.1.21 to Spring 6.2.11. There are over 50K beans in the ApplicationContext.

Steps to Reproduce:

  • Start application using Spring 6.1.21 – observe normal startup time.
  • Change only the Spring version to 6.2.11 – observe ~9–10 seconds delay during the refresh phase.
  • The issue is consistently reproducible by switching between these two versions.

Environment:

  • Spring Framework: 6.1.21 (baseline) vs. 6.2.11 (regression)
  • Local benchmark using JMH (see Results section below)
  • No other code changes between versions

Observed Behavior (Spring 6.2):

Multiple thread dumps taken during the refresh phase show the main thread consistently stuck in: - PlaceholderConfigurerSupport.doProcessProperties - PropertyPlaceholderHelper - PlaceholderParser

Expected Behavior (Spring 6.1):

  • Startup completes without delay.
  • Thread traces show smooth initialization and no significant time spent in property placeholder resolution.

Analysis:

  • The extended time in Spring 6.2 appears related to property placeholder resolution becoming a performance bottleneck.
  • This behavior was not present in 6.1 and is isolated to property processing logic during context initialization.
  • See the following from Spring 6.2 release notes:

    The parser for property placeholders has been completely rewritten to be as lenient as possible. As a result, certain constructs that previously worked by accident rather than by design are no longer possible.

Benchmark Setup:

Created a local benchmark repo using JMH to isolate the issue and confirm the regression.

Results:

=== Spring 6.1 Results ===
Benchmark                                                             (propertyCount)  Mode  Cnt   Score   Error  Units
SpringPropertyResolutionBenchmark.benchmarkBatchPropertyResolution              10000  avgt   15   5.371 ± 0.124  us/op
SpringPropertyResolutionBenchmark.benchmarkComplexPropertyResolution            10000  avgt   15  13.733 ± 0.138  us/op
SpringPropertyResolutionBenchmark.benchmarkNestedPropertyResolution             10000  avgt   15  11.659 ± 0.123  us/op
SpringPropertyResolutionBenchmark.benchmarkPropertyPlaceholderHelper            10000  avgt   15  11.875 ± 0.152  us/op
SpringPropertyResolutionBenchmark.benchmarkSimplePropertyResolution             10000  avgt   15   5.074 ± 0.103  us/op

=== Spring 6.2 Results ===
Benchmark                                                             (propertyCount)  Mode  Cnt   Score   Error  Units
SpringPropertyResolutionBenchmark.benchmarkBatchPropertyResolution              10000  avgt   15   8.391 ± 0.171  us/op
SpringPropertyResolutionBenchmark.benchmarkComplexPropertyResolution            10000  avgt   15  26.490 ± 0.730  us/op
SpringPropertyResolutionBenchmark.benchmarkNestedPropertyResolution             10000  avgt   15  20.151 ± 0.315  us/op
SpringPropertyResolutionBenchmark.benchmarkPropertyPlaceholderHelper            10000  avgt   15  23.800 ± 0.412  us/op
SpringPropertyResolutionBenchmark.benchmarkSimplePropertyResolution             10000  avgt   15   8.015 ± 0.078  us/op

Comment From: github-actions[bot]

Fixed via 449b85f446b390983931fe816bb33138f3ad146a

Comment From: jhoeller

Thanks for raising this, @rahulsh1 !

I've revised the PlaceholderParser implementation to avoid overhead for parsing plain values and simple placeholders, as far as possible. This brings the pure parser overhead in 6.2.12 down to ~20% compared to placeholder resolution performance in 6.1 (whereas plain parsing was in the ballpark of 3-4 times slower before). I hope that is good enough even for large-scale usage.

FWIW the remaining overhead is hard to optimize further since the flexibility and correctness of the re-implemented parser in 6.2 requires some additional state management.

These changes are available in the latest 6.2.12 snapshot now (https://repo.spring.io/snapshot). Please give it an early try!

Comment From: rahulsh1

Thanks for the quick response and prompt fix—really appreciate it! Most (90+%) of our usages are simple placeholders. Will give a try and update on the results.

Comment From: rahulsh1

No longer seeing the above regression with the fix. The timings for the refresh phase are back to what they were with Spring 6.1. Ran the JMH benchmark as well, the results seem to be much better too. Thanks!

=== Spring 6.1 Results ===
Benchmark                                                             (propertyCount)  Mode  Cnt   Score   Error  Units
SpringPropertyResolutionBenchmark.benchmarkBatchPropertyResolution              10000  avgt   15   5.381 ± 0.128  us/op
SpringPropertyResolutionBenchmark.benchmarkComplexPropertyResolution            10000  avgt   15  13.748 ± 0.166  us/op
SpringPropertyResolutionBenchmark.benchmarkNestedPropertyResolution             10000  avgt   15  11.609 ± 0.173  us/op
SpringPropertyResolutionBenchmark.benchmarkPropertyPlaceholderHelper            10000  avgt   15  12.109 ± 0.331  us/op
SpringPropertyResolutionBenchmark.benchmarkSimplePropertyResolution             10000  avgt   15   5.106 ± 0.091  us/op

=== Spring 6.2 Results ===
Benchmark                                                             (propertyCount)  Mode  Cnt   Score   Error  Units
SpringPropertyResolutionBenchmark.benchmarkBatchPropertyResolution              10000  avgt   15   5.079 ± 0.044  us/op
SpringPropertyResolutionBenchmark.benchmarkComplexPropertyResolution            10000  avgt   15  17.135 ± 0.465  us/op
SpringPropertyResolutionBenchmark.benchmarkNestedPropertyResolution             10000  avgt   15  14.194 ± 0.464  us/op
SpringPropertyResolutionBenchmark.benchmarkPropertyPlaceholderHelper            10000  avgt   15  16.600 ± 0.734  us/op
SpringPropertyResolutionBenchmark.benchmarkSimplePropertyResolution             10000  avgt   15   5.015 ± 0.111  us/op