Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8259065: Optimize MessageDigest.getInstance
Reviewed-by: valeriep
  • Loading branch information
cl4es committed Jan 8, 2021
1 parent 712014c commit fc1d2a1
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 136 deletions.
57 changes: 28 additions & 29 deletions src/java.base/share/classes/java/security/MessageDigest.java
Expand Up @@ -30,6 +30,7 @@
import java.io.PrintStream;
import java.nio.ByteBuffer;

import sun.security.jca.GetInstance;
import sun.security.util.Debug;
import sun.security.util.MessageDigestSpi2;

Expand Down Expand Up @@ -176,30 +177,27 @@ private MessageDigest(String algorithm, Provider p) {
* @see Provider
*/
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException {
throws NoSuchAlgorithmException
{
Objects.requireNonNull(algorithm, "null algorithm name");
try {
MessageDigest md;
Object[] objs = Security.getImpl(algorithm, "MessageDigest",
(String)null);
if (objs[0] instanceof MessageDigest) {
md = (MessageDigest)objs[0];
md.provider = (Provider)objs[1];
} else {
md = Delegate.of((MessageDigestSpi)objs[0], algorithm,
(Provider) objs[1]);
}

if (!skipDebug && pdebug != null) {
pdebug.println("MessageDigest." + algorithm +
" algorithm from: " + md.provider.getName());
}
MessageDigest md;

return md;
GetInstance.Instance instance = GetInstance.getInstance("MessageDigest",
MessageDigestSpi.class, algorithm);
if (instance.impl instanceof MessageDigest messageDigest) {
md = messageDigest;
md.provider = instance.provider;
} else {
md = Delegate.of((MessageDigestSpi)instance.impl, algorithm,
instance.provider);
}

} catch(NoSuchProviderException e) {
throw new NoSuchAlgorithmException(algorithm + " not found");
if (!skipDebug && pdebug != null) {
pdebug.println("MessageDigest." + algorithm +
" algorithm from: " + md.provider.getName());
}

return md;
}

/**
Expand Down Expand Up @@ -245,17 +243,18 @@ public static MessageDigest getInstance(String algorithm, String provider)
Objects.requireNonNull(algorithm, "null algorithm name");
if (provider == null || provider.isEmpty())
throw new IllegalArgumentException("missing provider");
Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
if (objs[0] instanceof MessageDigest) {
MessageDigest md = (MessageDigest)objs[0];
md.provider = (Provider)objs[1];
return md;

MessageDigest md;
GetInstance.Instance instance = GetInstance.getInstance("MessageDigest",
MessageDigestSpi.class, algorithm, provider);
if (instance.impl instanceof MessageDigest messageDigest) {
md = messageDigest;
md.provider = instance.provider;
} else {
MessageDigest delegate =
Delegate.of((MessageDigestSpi)objs[0], algorithm,
(Provider)objs[1]);
return delegate;
md = Delegate.of((MessageDigestSpi)instance.impl, algorithm,
instance.provider);
}
return md;
}

/**
Expand Down
160 changes: 99 additions & 61 deletions src/java.base/share/classes/java/security/Provider.java
Expand Up @@ -147,44 +147,7 @@ public abstract class Provider extends Properties {

private transient boolean initialized;

private static Object newInstanceUtil(final Class<?> clazz,
final Class<?> ctrParamClz, final Object ctorParamObj)
throws Exception {
if (ctrParamClz == null) {
Constructor<?> con = clazz.getConstructor();
return con.newInstance();
} else {
// Looking for the constructor with a params first and fallback
// to one without if not found. This is to support the enhanced
// SecureRandom where both styles of constructors are supported.
// Before jdk9, there was no params support (only getInstance(alg))
// and an impl only had the params-less constructor. Since jdk9,
// there is getInstance(alg,params) and an impl can contain
// an Impl(params) constructor.
try {
Constructor<?> con = clazz.getConstructor(ctrParamClz);
return con.newInstance(ctorParamObj);
} catch (NoSuchMethodException nsme) {
// For pre-jdk9 SecureRandom implementations, they only
// have params-less constructors which still works when
// the input ctorParamObj is null.
//
// For other primitives using params, ctorParamObj should not
// be null and nsme is thrown, just like before.
if (ctorParamObj == null) {
try {
Constructor<?> con = clazz.getConstructor();
return con.newInstance();
} catch (NoSuchMethodException nsme2) {
nsme.addSuppressed(nsme2);
throw nsme;
}
} else {
throw nsme;
}
}
}
}
private static final Object[] EMPTY = new Object[0];

private static double parseVersionStr(String s) {
try {
Expand Down Expand Up @@ -1106,16 +1069,15 @@ private ServiceKey(String type, String algorithm, boolean intern) {
this.algorithm = intern ? algorithm.intern() : algorithm;
}
public int hashCode() {
return Objects.hash(type, algorithm);
return type.hashCode() * 31 + algorithm.hashCode();
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ServiceKey)) {
if (!(obj instanceof ServiceKey other)) {
return false;
}
ServiceKey other = (ServiceKey)obj;
return this.type.equals(other.type)
&& this.algorithm.equals(other.algorithm);
}
Expand Down Expand Up @@ -1192,9 +1154,7 @@ private void parseLegacyPut(String name, String value) {
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
s = new Service(this, type, stdAlg);
legacyMap.put(key, s);
}
legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
Expand All @@ -1213,9 +1173,7 @@ private void parseLegacyPut(String name, String value) {
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
s = new Service(this, type, stdAlg);
legacyMap.put(key, s);
}
s.className = className;
Expand All @@ -1238,9 +1196,7 @@ private void parseLegacyPut(String name, String value) {
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
s = new Service(this, type, stdAlg);
legacyMap.put(key, s);
}
s.addAttribute(attributeName, attributeValue);
Expand Down Expand Up @@ -1673,14 +1629,24 @@ private static String getEngineName(String s) {
* @since 1.5
*/
public static class Service {

private String type, algorithm, className;
private final String type;
private final String algorithm;
private String className;
private final Provider provider;
private List<String> aliases;
private Map<UString,String> attributes;
private final EngineDescription engineDescription;

// Reference to the cached implementation Class object.
// Will be a Class if this service is loaded from the built-in
// classloader (unloading not possible), otherwise a WeakReference to a
// Class
private Object classCache;

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

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

private Service(Provider provider) {
private Service(Provider provider, String type, String algorithm) {
this.provider = provider;
this.type = type;
this.algorithm = algorithm;
engineDescription = knownEngines.get(type);
aliases = Collections.<String>emptyList();
attributes = Collections.<UString,String>emptyMap();
}
Expand Down Expand Up @@ -1749,6 +1718,7 @@ public Service(Provider provider, String type, String algorithm,
}
this.provider = provider;
this.type = getEngineName(type);
engineDescription = knownEngines.get(type);
this.algorithm = algorithm;
this.className = className;
if (aliases == null) {
Expand Down Expand Up @@ -1863,7 +1833,7 @@ public Object newInstance(Object constructorParameter)
}
Class<?> ctrParamClz;
try {
EngineDescription cap = knownEngines.get(type);
EngineDescription cap = engineDescription;
if (cap == null) {
// unknown engine type, use generic code
// this is the code path future for non-core
Expand All @@ -1890,7 +1860,7 @@ public Object newInstance(Object constructorParameter)
}
}
// constructorParameter can be null if not provided
return newInstanceUtil(getImplClass(), ctrParamClz, constructorParameter);
return newInstanceUtil(ctrParamClz, constructorParameter);
} catch (NoSuchAlgorithmException e) {
throw e;
} catch (InvocationTargetException e) {
Expand All @@ -1906,11 +1876,59 @@ public Object newInstance(Object constructorParameter)
}
}

private Object newInstanceOf() throws Exception {
Constructor<?> con = getDefaultConstructor();
return con.newInstance(EMPTY);
}

private Object newInstanceUtil(Class<?> ctrParamClz, Object ctorParamObj)
throws Exception
{
if (ctrParamClz == null) {
return newInstanceOf();
} else {
// Looking for the constructor with a params first and fallback
// to one without if not found. This is to support the enhanced
// SecureRandom where both styles of constructors are supported.
// Before jdk9, there was no params support (only getInstance(alg))
// and an impl only had the params-less constructor. Since jdk9,
// there is getInstance(alg,params) and an impl can contain
// an Impl(params) constructor.
try {
Constructor<?> con = getImplClass().getConstructor(ctrParamClz);
return con.newInstance(ctorParamObj);
} catch (NoSuchMethodException nsme) {
// For pre-jdk9 SecureRandom implementations, they only
// have params-less constructors which still works when
// the input ctorParamObj is null.
//
// For other primitives using params, ctorParamObj should not
// be null and nsme is thrown, just like before.
if (ctorParamObj == null) {
try {
return newInstanceOf();
} catch (NoSuchMethodException nsme2) {
nsme.addSuppressed(nsme2);
throw nsme;
}
} else {
throw nsme;
}
}
}
}

// return the implementation Class object for this service
private Class<?> getImplClass() throws NoSuchAlgorithmException {
try {
Reference<Class<?>> ref = classRef;
Class<?> clazz = (ref == null) ? null : ref.get();
Object cache = classCache;
if (cache instanceof Class<?> clazz) {
return clazz;
}
Class<?> clazz = null;
if (cache instanceof WeakReference<?> ref){
clazz = (Class<?>)ref.get();
}
if (clazz == null) {
ClassLoader cl = provider.getClass().getClassLoader();
if (cl == null) {
Expand All @@ -1923,7 +1941,7 @@ private Class<?> getImplClass() throws NoSuchAlgorithmException {
("class configured for " + type + " (provider: " +
provider.getName() + ") is not public.");
}
classRef = new WeakReference<>(clazz);
classCache = (cl == null) ? clazz : new WeakReference<Class<?>>(clazz);
}
return clazz;
} catch (ClassNotFoundException e) {
Expand All @@ -1933,6 +1951,26 @@ private Class<?> getImplClass() throws NoSuchAlgorithmException {
}
}

private Constructor<?> getDefaultConstructor()
throws NoSuchAlgorithmException, NoSuchMethodException
{
Object cache = constructorCache;
if (cache instanceof Constructor<?> con) {
return con;
}
Constructor<?> con = null;
if (cache instanceof WeakReference<?> ref){
con = (Constructor<?>)ref.get();
}
if (con == null) {
Class<?> clazz = getImplClass();
con = clazz.getConstructor();
constructorCache = (clazz.getClassLoader() == null)
? con : new WeakReference<Constructor<?>>(con);
}
return con;
}

/**
* Test whether this Service can use the specified parameter.
* Returns false if this service cannot use the parameter. Returns
Expand Down Expand Up @@ -1960,7 +1998,7 @@ private Class<?> getImplClass() throws NoSuchAlgorithmException {
* used with this type of service
*/
public boolean supportsParameter(Object parameter) {
EngineDescription cap = knownEngines.get(type);
EngineDescription cap = engineDescription;
if (cap == null) {
// unknown engine type, return true by default
return true;
Expand Down

1 comment on commit fc1d2a1

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.