Skip to content

Commit fc1d2a1

Browse files
committedJan 8, 2021
8259065: Optimize MessageDigest.getInstance
Reviewed-by: valeriep
1 parent 712014c commit fc1d2a1

File tree

4 files changed

+258
-136
lines changed

4 files changed

+258
-136
lines changed
 

‎src/java.base/share/classes/java/security/MessageDigest.java

+28-29
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.io.PrintStream;
3131
import java.nio.ByteBuffer;
3232

33+
import sun.security.jca.GetInstance;
3334
import sun.security.util.Debug;
3435
import sun.security.util.MessageDigestSpi2;
3536

@@ -176,30 +177,27 @@ private MessageDigest(String algorithm, Provider p) {
176177
* @see Provider
177178
*/
178179
public static MessageDigest getInstance(String algorithm)
179-
throws NoSuchAlgorithmException {
180+
throws NoSuchAlgorithmException
181+
{
180182
Objects.requireNonNull(algorithm, "null algorithm name");
181-
try {
182-
MessageDigest md;
183-
Object[] objs = Security.getImpl(algorithm, "MessageDigest",
184-
(String)null);
185-
if (objs[0] instanceof MessageDigest) {
186-
md = (MessageDigest)objs[0];
187-
md.provider = (Provider)objs[1];
188-
} else {
189-
md = Delegate.of((MessageDigestSpi)objs[0], algorithm,
190-
(Provider) objs[1]);
191-
}
192-
193-
if (!skipDebug && pdebug != null) {
194-
pdebug.println("MessageDigest." + algorithm +
195-
" algorithm from: " + md.provider.getName());
196-
}
183+
MessageDigest md;
197184

198-
return md;
185+
GetInstance.Instance instance = GetInstance.getInstance("MessageDigest",
186+
MessageDigestSpi.class, algorithm);
187+
if (instance.impl instanceof MessageDigest messageDigest) {
188+
md = messageDigest;
189+
md.provider = instance.provider;
190+
} else {
191+
md = Delegate.of((MessageDigestSpi)instance.impl, algorithm,
192+
instance.provider);
193+
}
199194

200-
} catch(NoSuchProviderException e) {
201-
throw new NoSuchAlgorithmException(algorithm + " not found");
195+
if (!skipDebug && pdebug != null) {
196+
pdebug.println("MessageDigest." + algorithm +
197+
" algorithm from: " + md.provider.getName());
202198
}
199+
200+
return md;
203201
}
204202

205203
/**
@@ -245,17 +243,18 @@ public static MessageDigest getInstance(String algorithm, String provider)
245243
Objects.requireNonNull(algorithm, "null algorithm name");
246244
if (provider == null || provider.isEmpty())
247245
throw new IllegalArgumentException("missing provider");
248-
Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
249-
if (objs[0] instanceof MessageDigest) {
250-
MessageDigest md = (MessageDigest)objs[0];
251-
md.provider = (Provider)objs[1];
252-
return md;
246+
247+
MessageDigest md;
248+
GetInstance.Instance instance = GetInstance.getInstance("MessageDigest",
249+
MessageDigestSpi.class, algorithm, provider);
250+
if (instance.impl instanceof MessageDigest messageDigest) {
251+
md = messageDigest;
252+
md.provider = instance.provider;
253253
} else {
254-
MessageDigest delegate =
255-
Delegate.of((MessageDigestSpi)objs[0], algorithm,
256-
(Provider)objs[1]);
257-
return delegate;
254+
md = Delegate.of((MessageDigestSpi)instance.impl, algorithm,
255+
instance.provider);
258256
}
257+
return md;
259258
}
260259

261260
/**

‎src/java.base/share/classes/java/security/Provider.java

+99-61
Original file line numberDiff line numberDiff line change
@@ -147,44 +147,7 @@ public abstract class Provider extends Properties {
147147

148148
private transient boolean initialized;
149149

150-
private static Object newInstanceUtil(final Class<?> clazz,
151-
final Class<?> ctrParamClz, final Object ctorParamObj)
152-
throws Exception {
153-
if (ctrParamClz == null) {
154-
Constructor<?> con = clazz.getConstructor();
155-
return con.newInstance();
156-
} else {
157-
// Looking for the constructor with a params first and fallback
158-
// to one without if not found. This is to support the enhanced
159-
// SecureRandom where both styles of constructors are supported.
160-
// Before jdk9, there was no params support (only getInstance(alg))
161-
// and an impl only had the params-less constructor. Since jdk9,
162-
// there is getInstance(alg,params) and an impl can contain
163-
// an Impl(params) constructor.
164-
try {
165-
Constructor<?> con = clazz.getConstructor(ctrParamClz);
166-
return con.newInstance(ctorParamObj);
167-
} catch (NoSuchMethodException nsme) {
168-
// For pre-jdk9 SecureRandom implementations, they only
169-
// have params-less constructors which still works when
170-
// the input ctorParamObj is null.
171-
//
172-
// For other primitives using params, ctorParamObj should not
173-
// be null and nsme is thrown, just like before.
174-
if (ctorParamObj == null) {
175-
try {
176-
Constructor<?> con = clazz.getConstructor();
177-
return con.newInstance();
178-
} catch (NoSuchMethodException nsme2) {
179-
nsme.addSuppressed(nsme2);
180-
throw nsme;
181-
}
182-
} else {
183-
throw nsme;
184-
}
185-
}
186-
}
187-
}
150+
private static final Object[] EMPTY = new Object[0];
188151

189152
private static double parseVersionStr(String s) {
190153
try {
@@ -1106,16 +1069,15 @@ private ServiceKey(String type, String algorithm, boolean intern) {
11061069
this.algorithm = intern ? algorithm.intern() : algorithm;
11071070
}
11081071
public int hashCode() {
1109-
return Objects.hash(type, algorithm);
1072+
return type.hashCode() * 31 + algorithm.hashCode();
11101073
}
11111074
public boolean equals(Object obj) {
11121075
if (this == obj) {
11131076
return true;
11141077
}
1115-
if (!(obj instanceof ServiceKey)) {
1078+
if (!(obj instanceof ServiceKey other)) {
11161079
return false;
11171080
}
1118-
ServiceKey other = (ServiceKey)obj;
11191081
return this.type.equals(other.type)
11201082
&& this.algorithm.equals(other.algorithm);
11211083
}
@@ -1192,9 +1154,7 @@ private void parseLegacyPut(String name, String value) {
11921154
ServiceKey key = new ServiceKey(type, stdAlg, true);
11931155
Service s = legacyMap.get(key);
11941156
if (s == null) {
1195-
s = new Service(this);
1196-
s.type = type;
1197-
s.algorithm = stdAlg;
1157+
s = new Service(this, type, stdAlg);
11981158
legacyMap.put(key, s);
11991159
}
12001160
legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
@@ -1213,9 +1173,7 @@ private void parseLegacyPut(String name, String value) {
12131173
ServiceKey key = new ServiceKey(type, stdAlg, true);
12141174
Service s = legacyMap.get(key);
12151175
if (s == null) {
1216-
s = new Service(this);
1217-
s.type = type;
1218-
s.algorithm = stdAlg;
1176+
s = new Service(this, type, stdAlg);
12191177
legacyMap.put(key, s);
12201178
}
12211179
s.className = className;
@@ -1238,9 +1196,7 @@ private void parseLegacyPut(String name, String value) {
12381196
ServiceKey key = new ServiceKey(type, stdAlg, true);
12391197
Service s = legacyMap.get(key);
12401198
if (s == null) {
1241-
s = new Service(this);
1242-
s.type = type;
1243-
s.algorithm = stdAlg;
1199+
s = new Service(this, type, stdAlg);
12441200
legacyMap.put(key, s);
12451201
}
12461202
s.addAttribute(attributeName, attributeValue);
@@ -1673,14 +1629,24 @@ private static String getEngineName(String s) {
16731629
* @since 1.5
16741630
*/
16751631
public static class Service {
1676-
1677-
private String type, algorithm, className;
1632+
private final String type;
1633+
private final String algorithm;
1634+
private String className;
16781635
private final Provider provider;
16791636
private List<String> aliases;
16801637
private Map<UString,String> attributes;
1638+
private final EngineDescription engineDescription;
1639+
1640+
// Reference to the cached implementation Class object.
1641+
// Will be a Class if this service is loaded from the built-in
1642+
// classloader (unloading not possible), otherwise a WeakReference to a
1643+
// Class
1644+
private Object classCache;
16811645

1682-
// Reference to the cached implementation Class object
1683-
private volatile Reference<Class<?>> classRef;
1646+
// Will be a Constructor if this service is loaded from the built-in
1647+
// classloader (unloading not possible), otherwise a WeakReference to
1648+
// a Constructor
1649+
private Object constructorCache;
16841650

16851651
// flag indicating whether this service has its attributes for
16861652
// supportedKeyFormats or supportedKeyClasses set
@@ -1702,8 +1668,11 @@ public static class Service {
17021668
// this constructor and these methods are used for parsing
17031669
// the legacy string properties.
17041670

1705-
private Service(Provider provider) {
1671+
private Service(Provider provider, String type, String algorithm) {
17061672
this.provider = provider;
1673+
this.type = type;
1674+
this.algorithm = algorithm;
1675+
engineDescription = knownEngines.get(type);
17071676
aliases = Collections.<String>emptyList();
17081677
attributes = Collections.<UString,String>emptyMap();
17091678
}
@@ -1749,6 +1718,7 @@ public Service(Provider provider, String type, String algorithm,
17491718
}
17501719
this.provider = provider;
17511720
this.type = getEngineName(type);
1721+
engineDescription = knownEngines.get(type);
17521722
this.algorithm = algorithm;
17531723
this.className = className;
17541724
if (aliases == null) {
@@ -1863,7 +1833,7 @@ public Object newInstance(Object constructorParameter)
18631833
}
18641834
Class<?> ctrParamClz;
18651835
try {
1866-
EngineDescription cap = knownEngines.get(type);
1836+
EngineDescription cap = engineDescription;
18671837
if (cap == null) {
18681838
// unknown engine type, use generic code
18691839
// this is the code path future for non-core
@@ -1890,7 +1860,7 @@ public Object newInstance(Object constructorParameter)
18901860
}
18911861
}
18921862
// constructorParameter can be null if not provided
1893-
return newInstanceUtil(getImplClass(), ctrParamClz, constructorParameter);
1863+
return newInstanceUtil(ctrParamClz, constructorParameter);
18941864
} catch (NoSuchAlgorithmException e) {
18951865
throw e;
18961866
} catch (InvocationTargetException e) {
@@ -1906,11 +1876,59 @@ public Object newInstance(Object constructorParameter)
19061876
}
19071877
}
19081878

1879+
private Object newInstanceOf() throws Exception {
1880+
Constructor<?> con = getDefaultConstructor();
1881+
return con.newInstance(EMPTY);
1882+
}
1883+
1884+
private Object newInstanceUtil(Class<?> ctrParamClz, Object ctorParamObj)
1885+
throws Exception
1886+
{
1887+
if (ctrParamClz == null) {
1888+
return newInstanceOf();
1889+
} else {
1890+
// Looking for the constructor with a params first and fallback
1891+
// to one without if not found. This is to support the enhanced
1892+
// SecureRandom where both styles of constructors are supported.
1893+
// Before jdk9, there was no params support (only getInstance(alg))
1894+
// and an impl only had the params-less constructor. Since jdk9,
1895+
// there is getInstance(alg,params) and an impl can contain
1896+
// an Impl(params) constructor.
1897+
try {
1898+
Constructor<?> con = getImplClass().getConstructor(ctrParamClz);
1899+
return con.newInstance(ctorParamObj);
1900+
} catch (NoSuchMethodException nsme) {
1901+
// For pre-jdk9 SecureRandom implementations, they only
1902+
// have params-less constructors which still works when
1903+
// the input ctorParamObj is null.
1904+
//
1905+
// For other primitives using params, ctorParamObj should not
1906+
// be null and nsme is thrown, just like before.
1907+
if (ctorParamObj == null) {
1908+
try {
1909+
return newInstanceOf();
1910+
} catch (NoSuchMethodException nsme2) {
1911+
nsme.addSuppressed(nsme2);
1912+
throw nsme;
1913+
}
1914+
} else {
1915+
throw nsme;
1916+
}
1917+
}
1918+
}
1919+
}
1920+
19091921
// return the implementation Class object for this service
19101922
private Class<?> getImplClass() throws NoSuchAlgorithmException {
19111923
try {
1912-
Reference<Class<?>> ref = classRef;
1913-
Class<?> clazz = (ref == null) ? null : ref.get();
1924+
Object cache = classCache;
1925+
if (cache instanceof Class<?> clazz) {
1926+
return clazz;
1927+
}
1928+
Class<?> clazz = null;
1929+
if (cache instanceof WeakReference<?> ref){
1930+
clazz = (Class<?>)ref.get();
1931+
}
19141932
if (clazz == null) {
19151933
ClassLoader cl = provider.getClass().getClassLoader();
19161934
if (cl == null) {
@@ -1923,7 +1941,7 @@ private Class<?> getImplClass() throws NoSuchAlgorithmException {
19231941
("class configured for " + type + " (provider: " +
19241942
provider.getName() + ") is not public.");
19251943
}
1926-
classRef = new WeakReference<>(clazz);
1944+
classCache = (cl == null) ? clazz : new WeakReference<Class<?>>(clazz);
19271945
}
19281946
return clazz;
19291947
} catch (ClassNotFoundException e) {
@@ -1933,6 +1951,26 @@ private Class<?> getImplClass() throws NoSuchAlgorithmException {
19331951
}
19341952
}
19351953

1954+
private Constructor<?> getDefaultConstructor()
1955+
throws NoSuchAlgorithmException, NoSuchMethodException
1956+
{
1957+
Object cache = constructorCache;
1958+
if (cache instanceof Constructor<?> con) {
1959+
return con;
1960+
}
1961+
Constructor<?> con = null;
1962+
if (cache instanceof WeakReference<?> ref){
1963+
con = (Constructor<?>)ref.get();
1964+
}
1965+
if (con == null) {
1966+
Class<?> clazz = getImplClass();
1967+
con = clazz.getConstructor();
1968+
constructorCache = (clazz.getClassLoader() == null)
1969+
? con : new WeakReference<Constructor<?>>(con);
1970+
}
1971+
return con;
1972+
}
1973+
19361974
/**
19371975
* Test whether this Service can use the specified parameter.
19381976
* Returns false if this service cannot use the parameter. Returns
@@ -1960,7 +1998,7 @@ private Class<?> getImplClass() throws NoSuchAlgorithmException {
19601998
* used with this type of service
19611999
*/
19622000
public boolean supportsParameter(Object parameter) {
1963-
EngineDescription cap = knownEngines.get(type);
2001+
EngineDescription cap = engineDescription;
19642002
if (cap == null) {
19652003
// unknown engine type, return true by default
19662004
return true;

‎src/java.base/share/classes/sun/security/jca/ProviderConfig.java

+53-46
Original file line numberDiff line numberDiff line change
@@ -160,66 +160,73 @@ public String toString() {
160160
* Get the provider object. Loads the provider if it is not already loaded.
161161
*/
162162
@SuppressWarnings("deprecation")
163-
synchronized Provider getProvider() {
163+
Provider getProvider() {
164164
// volatile variable load
165165
Provider p = provider;
166166
if (p != null) {
167167
return p;
168168
}
169-
if (shouldLoad() == false) {
170-
return null;
171-
}
169+
// DCL
170+
synchronized (this) {
171+
p = provider;
172+
if (p != null) {
173+
return p;
174+
}
175+
if (shouldLoad() == false) {
176+
return null;
177+
}
172178

173-
// Create providers which are in java.base directly
174-
if (provName.equals("SUN") || provName.equals("sun.security.provider.Sun")) {
175-
p = new sun.security.provider.Sun();
176-
} else if (provName.equals("SunRsaSign") || provName.equals("sun.security.rsa.SunRsaSign")) {
177-
p = new sun.security.rsa.SunRsaSign();
178-
} else if (provName.equals("SunJCE") || provName.equals("com.sun.crypto.provider.SunJCE")) {
179-
p = new com.sun.crypto.provider.SunJCE();
180-
} else if (provName.equals("SunJSSE")) {
181-
p = new sun.security.ssl.SunJSSE();
182-
} else if (provName.equals("Apple") || provName.equals("apple.security.AppleProvider")) {
183-
// need to use reflection since this class only exists on MacOsx
184-
p = AccessController.doPrivileged(new PrivilegedAction<Provider>() {
185-
public Provider run() {
186-
try {
187-
Class<?> c = Class.forName("apple.security.AppleProvider");
188-
if (Provider.class.isAssignableFrom(c)) {
189-
@SuppressWarnings("deprecation")
190-
Object tmp = c.newInstance();
191-
return (Provider) tmp;
192-
} else {
179+
// Create providers which are in java.base directly
180+
if (provName.equals("SUN") || provName.equals("sun.security.provider.Sun")) {
181+
p = new sun.security.provider.Sun();
182+
} else if (provName.equals("SunRsaSign") || provName.equals("sun.security.rsa.SunRsaSign")) {
183+
p = new sun.security.rsa.SunRsaSign();
184+
} else if (provName.equals("SunJCE") || provName.equals("com.sun.crypto.provider.SunJCE")) {
185+
p = new com.sun.crypto.provider.SunJCE();
186+
} else if (provName.equals("SunJSSE")) {
187+
p = new sun.security.ssl.SunJSSE();
188+
} else if (provName.equals("Apple") || provName.equals("apple.security.AppleProvider")) {
189+
// need to use reflection since this class only exists on MacOsx
190+
p = AccessController.doPrivileged(new PrivilegedAction<Provider>() {
191+
public Provider run() {
192+
try {
193+
Class<?> c = Class.forName("apple.security.AppleProvider");
194+
if (Provider.class.isAssignableFrom(c)) {
195+
@SuppressWarnings("deprecation")
196+
Object tmp = c.newInstance();
197+
return (Provider) tmp;
198+
} else {
199+
return null;
200+
}
201+
} catch (Exception ex) {
202+
if (debug != null) {
203+
debug.println("Error loading provider Apple");
204+
ex.printStackTrace();
205+
}
193206
return null;
194207
}
195-
} catch (Exception ex) {
196-
if (debug != null) {
197-
debug.println("Error loading provider Apple");
198-
ex.printStackTrace();
208+
}
209+
});
210+
} else {
211+
if (isLoading) {
212+
// because this method is synchronized, this can only
213+
// happen if there is recursion.
214+
if (debug != null) {
215+
debug.println("Recursion loading provider: " + this);
216+
new Exception("Call trace").printStackTrace();
199217
}
200218
return null;
201219
}
202-
}
203-
});
204-
} else {
205-
if (isLoading) {
206-
// because this method is synchronized, this can only
207-
// happen if there is recursion.
208-
if (debug != null) {
209-
debug.println("Recursion loading provider: " + this);
210-
new Exception("Call trace").printStackTrace();
220+
try {
221+
isLoading = true;
222+
tries++;
223+
p = doLoadProvider();
224+
} finally {
225+
isLoading = false;
211226
}
212-
return null;
213-
}
214-
try {
215-
isLoading = true;
216-
tries++;
217-
p = doLoadProvider();
218-
} finally {
219-
isLoading = false;
220227
}
228+
provider = p;
221229
}
222-
provider = p;
223230
return p;
224231
}
225232

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package org.openjdk.bench.java.security;
24+
25+
import java.security.DigestException;
26+
import java.security.MessageDigest;
27+
import java.security.NoSuchAlgorithmException;
28+
import java.security.NoSuchProviderException;
29+
import java.util.Random;
30+
import java.util.concurrent.TimeUnit;
31+
import org.openjdk.jmh.annotations.Benchmark;
32+
import org.openjdk.jmh.annotations.BenchmarkMode;
33+
import org.openjdk.jmh.annotations.Fork;
34+
import org.openjdk.jmh.annotations.Measurement;
35+
import org.openjdk.jmh.annotations.Mode;
36+
import org.openjdk.jmh.annotations.OutputTimeUnit;
37+
import org.openjdk.jmh.annotations.Param;
38+
import org.openjdk.jmh.annotations.Scope;
39+
import org.openjdk.jmh.annotations.Setup;
40+
import org.openjdk.jmh.annotations.State;
41+
import org.openjdk.jmh.annotations.Warmup;
42+
43+
/**
44+
* Micros for speed of looking up and instantiating MessageDigests.
45+
*/
46+
@State(Scope.Thread)
47+
@BenchmarkMode(Mode.AverageTime)
48+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
49+
@Warmup(iterations = 5, time = 1)
50+
@Measurement(iterations = 10, time = 1)
51+
@Fork(value = 3)
52+
public class GetMessageDigest {
53+
54+
@Param({"md5", "SHA-1", "SHA-256"})
55+
private String digesterName;
56+
57+
private MessageDigest messageDigest;
58+
59+
@Setup
60+
public void setupMessageDigestForCloning() throws NoSuchAlgorithmException {
61+
messageDigest = MessageDigest.getInstance(digesterName);
62+
}
63+
64+
@Benchmark
65+
public MessageDigest getInstance() throws NoSuchAlgorithmException {
66+
return MessageDigest.getInstance(digesterName);
67+
}
68+
69+
@Benchmark
70+
public MessageDigest cloneInstance() throws NoSuchAlgorithmException, CloneNotSupportedException {
71+
return (MessageDigest)messageDigest.clone();
72+
}
73+
74+
@Benchmark
75+
public MessageDigest getInstanceWithProvider() throws NoSuchAlgorithmException, NoSuchProviderException {
76+
return MessageDigest.getInstance(digesterName, "SUN");
77+
}
78+
}

0 commit comments

Comments
 (0)
Please sign in to comment.