JDK-8276660 : Scalability bottleneck in java.security.Provider.getService()
  • Type: Enhancement
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 17,18
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2021-11-04
  • Updated: 2025-04-08
  • Resolved: 2021-12-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 17 JDK 18 JDK 19
17.0.13Fixed 18 b28Fixed 19Fixed
Related Reports
Causes :  
Duplicate :  
Relates :  
Relates :  
Description
Multiple callers of Cipher.getInstance() can cause scalability bottleneck with large numbers of cores/threads.

This can be seen with SPECjvm crypto.rsa with very large numbers of cores/threads. 
Comments
[jdk17u-fix-request] Approval Request from Dan Lutker Backporting JDK-8276660 to fix issue with Provider registration not taking immediate effect. Ran tier1-4 and reproducer of the issue without a problem. Also pulling in followups of JDK-8292739 and JDK-8279222 as to not introduce bugs. This was not clean and required reverting and re-applying JDK-8254711 to get the correct code.
23-07-2024

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk17u-dev/pull/2730 Date: 2024-07-19 18:46:32 +0000
22-07-2024

Changeset: 9b747491 Author: Valerie Peng <valeriep@openjdk.org> Date: 2021-12-08 17:44:10 +0000 URL: https://git.openjdk.java.net/jdk/commit/9b747491de01fd011b09668a67113e80c2b7c708
08-12-2021

One way to address this problem is to change the legacy registration to utilize a concurrentHashMap and get rid of the synchronization block in the Provider.getService(String, String) method. Instead of lazily parse the legacy entries when service is requested, every legacy registration call, i.e. put(String, String), will be parsed and results are stored into 'legacyMap' eagerly. This will resolve the synchronization block overhead in Provider.getService() for providers who do not use legacy registration.
06-11-2021

The current impl of Provider.getService(String, String) method will first search 'serviceMap' (populated using putService(...) calls) for a matching service, if none found, then it falls through to the legacy registration which is guarded by a synchronizing on the current Provider object. Existing JDK providers all use putService(...) calls and the matching service would be found by searching 'serviceMap'. However, when a provider is requested with a service which it has no support, the 'serviceMap' search would fail and the search continues down to the synchronization block which triggers that parsing and lookup of legacy registration. Although the default JDK providers have no legacy registration, it'd still enter that synchronization block which causes performance bottleneck when there are large numbers of cores/threads. For SPECjvm crypto.rsa benchmark, to get to RSA Cipher in SunJCE provider, the search would first try SUN, SunRsaSign, SunEC, SunJSSE providers and going through their provider synchronization blocks inside the getService() method before finding the supported impl from SunJCE provider 'serviceMap'.
06-11-2021