Skip to content

Commit

Permalink
Merge pull request #515 from pshipton/classcache
Browse files Browse the repository at this point in the history
Rename CacheClass to ClassByNameCache to avoid OpenJDK conflict
  • Loading branch information
keithc-ca committed May 4, 2022
2 parents ae47d54 + 945f079 commit 9915781
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2017, 2019 All Rights Reserved
* ===========================================================================
*
* ===========================================================================
* (c) Copyright IBM Corp. 2017, 2022 All Rights Reserved
* ===========================================================================
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
* published by the Free Software Foundation.
*
* IBM designates this particular file as subject to the "Classpath" exception
* IBM designates this particular file as subject to the "Classpath" exception
* as provided by IBM in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
Expand All @@ -18,9 +18,9 @@
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*
*
* ===========================================================================
*/
*/

package java.io;

Expand All @@ -30,19 +30,18 @@
import java.security.PrivilegedAction;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/* ClassCache is Primarily responsible for Caching the results of the className lookups and hence to avoid
/* ClassByNameCache is Primarily responsible for Caching the results of the className lookups and hence to avoid
* multiple Lookup for same Class Instance.
* ClassCache provides a ConcurrentHash based ClassCache which is looked up prior to calling the class.forName
* ClassByNameCache provides a ConcurrentHash based ClassByNameCache which is looked up prior to calling the class.forName
* Method resolveClass() from ObjectInputStream uses this Cache.
*
*
* Caching is done only when the actually used loader for a Class is one of the Sytem loaders (ie) App Class Loader,
* System Extension loader and BootStrap loader
*
* System Extension loader and BootStrap loader.
*/
final class ClassCache {
final class ClassByNameCache {
/* Main Cache for storing the Class.forName results Here Key used would be CacheKey */
private final ConcurrentHashMap<Key,Object> cache =
new ConcurrentHashMap<Key,Object>();
new ConcurrentHashMap<Key,Object>();
/* Initiating Loader to CacheKey mapping in the Cache used by Reaper thread for removal on stale Loaders */
private final ConcurrentHashMap<LoaderRef,CacheKey> loaderKeys =
new ConcurrentHashMap<LoaderRef,CacheKey>();
Expand All @@ -56,9 +55,9 @@ final class ClassCache {
* Constructor Populates Canonical Loader Refs with System Loader Entries and initializes the reaper thread which
* monitors the ReferenceQueue for stale loaders
*/
public ClassCache() {
ClassLoader loader = ClassLoader.getSystemClassLoader();

public ClassByNameCache() {
ClassLoader loader = ClassLoader.getSystemClassLoader();
while (loader != null) {
setCanonicalSystemLoaderRef(loader);
loader = loader.getParent();
Expand All @@ -75,7 +74,7 @@ private void setCanonicalSystemLoaderRef(ClassLoader loader) {
LoaderRef newKey = new LoaderRef(loader, staleLoaderRefs, true);
assert (canonicalLoaderRefs.put(newKey, newKey) == null);
}

/*
* get Canonical Loader reference for the loader
*/
Expand Down Expand Up @@ -108,7 +107,7 @@ void removeStaleRef(LoaderRef loaderRef) {
}

/*
* Identifies if the loader used to load is one of the system loaders,
* Identifies if the loader used to load is one of the system loaders,
* if so updates the cache and the LoaderKey, and also a StaleLoaderReference for the initiating LoaderObject
* via LoaderRef Constructor
*/
Expand Down Expand Up @@ -137,7 +136,7 @@ void update(CacheKey key, Class<?> result) {
}
}
/*
* Creates a New Entry in the Cache
* Creates a New Entry in the Cache
*/
private Object createEntry(CacheKey key) {
FutureValue newValue = new FutureValue(key, this); //Does actual call to class.forName as required.
Expand All @@ -146,7 +145,7 @@ private Object createEntry(CacheKey key) {
return value;
}
/*
* This is the entry point in to the cache from ObjectInputStream. First Lookup is done based on the className and Loader
* This is the entry point in to the cache from ObjectInputStream. First Lookup is done based on the className and Loader
*/
public Class<?> get(String className, ClassLoader loader)
throws ClassNotFoundException {
Expand All @@ -157,24 +156,24 @@ public Class<?> get(String className, ClassLoader loader)
}

if (value instanceof FutureValue) {

return ((FutureValue)value).get();
}

return (Class<?>)value;
}

/*
* FutureValue implements Future Mechanics that is required for addressing contention issues in the HashMap
*/

private static final class FutureValue {
private final CacheKey key;
private final LoaderRef loaderRef;
private final ClassCache cache;
private final ClassByNameCache cache;
private Class<?> value = null;

FutureValue(CacheKey key, ClassCache cache) {
FutureValue(CacheKey key, ClassByNameCache cache) {
this.key = key;
this.loaderRef = key.loaderRef;
this.cache = cache;
Expand All @@ -200,27 +199,27 @@ Class<?> get() throws ClassNotFoundException {

private static final class CreateReaperAction
implements PrivilegedAction<Thread> {
private final ClassCache cache;
private final ClassByNameCache cache;
private final ReferenceQueue<Object> queue;

CreateReaperAction(ClassCache cache, ReferenceQueue<Object> queue) {
CreateReaperAction(ClassByNameCache cache, ReferenceQueue<Object> queue) {
this.cache = cache;
this.queue = queue;
}

public Thread run() {
Reaper reaper = new Reaper(cache, queue);
return com.ibm.oti.vm.VM.getVMLangAccess().createThread(reaper, "ClassCache Reaper", true, false, true, null);
return com.ibm.oti.vm.VM.getVMLangAccess().createThread(reaper, "ClassByNameCache Reaper", true, false, true, null);
}
}

private static final class Reaper implements Runnable {
private final WeakReference<ClassCache> cacheRef;
private final WeakReference<ClassByNameCache> cacheRef;
private final ReferenceQueue<Object> queue;

Reaper(ClassCache cache, ReferenceQueue<Object> queue) {
Reaper(ClassByNameCache cache, ReferenceQueue<Object> queue) {
this.queue = queue;
cacheRef = new WeakReference<ClassCache>(cache, queue);
cacheRef = new WeakReference<ClassByNameCache>(cache, queue);
}

/*
Expand All @@ -240,24 +239,24 @@ public void run() {
}

private void processStaleRef(LoaderRef staleRef) {
ClassCache cache = cacheRef.get();
ClassByNameCache cache = cacheRef.get();
if (cache == null) return;

cache.removeStaleRef(staleRef);
}
}

/*
* The use of the loaderRefs map is to allow efficient processing
* of one Weak Reference for each stale ClassLoader, rather than one WR for each entry in the cache.
*
* of one Weak Reference for each stale ClassLoader, rather than one WR for each entry in the cache.
*
* CacheKey as well as Lookup Key will be refering to this LoaderRef.
*
* Initiating Class Loaders needs to be referred for both lookup and Caching,so for performance reasons
* a LoaderRef is maintained which would be used by both LookupKey and CachingKey
* (ie) LookupKey will actually store the LoaderObj, but it will be canonically refered via a loaderRed
* by the caching Key
* LoaderKey has LoaderRef Objects as well and is used to Link the Initiating Loader with the actual cache Entries
* LoaderKey has LoaderRef Objects as well and is used to Link the Initiating Loader with the actual cache Entries
* which is used to remove Stale reference entries.
*/

Expand All @@ -278,9 +277,9 @@ static Object getLoaderObj(ClassLoader loader) {
boolean isSystem) {
this(isSystem, getLoaderObj(loader), queue);
}

/*
* Creates a new weak reference that refers to the given object and is registered with the given queue.
* Creates a new weak reference that refers to the given object and is registered with the given queue.
*/

private LoaderRef(boolean isSystem, Object loaderObj,
Expand Down Expand Up @@ -313,9 +312,9 @@ ClassLoader getActiveLoader() {
/*
* For better clarity and to avoid multiple lookups to the cache. Key is implemented to have
* one abstract key to final sub classes which serve specific purpose
* LookupKey - This is a short lived key, not part of any hashmap and stores the strong reference to
* LookupKey - This is a short lived key, not part of any hashmap and stores the strong reference to
* loaderobject
* CachingKey - uses the same hash as LookupKey and has a means to be generated from LookupKey and has reference
* CachingKey - uses the same hash as LookupKey and has a means to be generated from LookupKey and has reference
* to the Loaderobj via a weakreference.
*/

Expand Down Expand Up @@ -345,13 +344,13 @@ public final int hashCode() {
}
}
/*
* Lookup Key hash code is framed using loadername hash + loader's system identity hashcode. This
* Lookup Key hash code is framed using loadername hash + loader's system identity hashcode. This
* is same as the hashcode maintained in CacheKey
*/

private static final class LookupKey extends Key {
private final Object loaderObj;
private final ClassCache cache;
private final ClassByNameCache cache;

private static int hashCode(String className, ClassLoader loader) {
int hashcode = className.hashCode();
Expand All @@ -363,7 +362,7 @@ private static int hashCode(String className, ClassLoader loader) {
}

public LookupKey(String className, ClassLoader loader,
ClassCache cache) {
ClassByNameCache cache) {
super(Objects.requireNonNull(className),
hashCode(className, loader));
loaderObj = LoaderRef.getLoaderObj(loader);
Expand All @@ -381,9 +380,9 @@ CacheKey createCacheKey() {
}

/*
* CacheKey is the actual key that is stored in the cache, and it stores the weakreference
* CacheKey is the actual key that is stored in the cache, and it stores the weakreference
* of the Initiating loader object via loaderRef
*
*
*/
private static final class CacheKey extends Key {
public final LoaderRef loaderRef;
Expand Down
12 changes: 6 additions & 6 deletions src/java.base/share/classes/java/io/ObjectInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2018, 2020 All Rights Reserved
* (c) Copyright IBM Corp. 2018, 2022 All Rights Reserved
* ===========================================================================
*/

Expand Down Expand Up @@ -354,13 +354,13 @@ private static class Logging {
* read* requests
*/

/* ClassCache Entry for caching class.forName results upon enableClassCaching */
private static final ClassCache classCache;
/* ClassByNameCache Entry for caching class.forName results upon enableClassCaching */
private static final ClassByNameCache classByNameCache;
private static final boolean isClassCachingEnabled;
static {
isClassCachingEnabled =
AccessController.doPrivileged(new GetClassCachingSettingAction());
classCache = (isClassCachingEnabled ? new ClassCache() : null);
classByNameCache = (isClassCachingEnabled ? new ClassByNameCache() : null);
}


Expand Down Expand Up @@ -867,14 +867,14 @@ protected Class<?> resolveClass(ObjectStreamClass desc)
{
String name = desc.getName();
try {
if (null == classCache) {
if (null == classByNameCache) {
return Class.forName(name, false, latestUserDefinedLoader());
} else {
if (refreshLudcl) {
cachedLudcl = latestUserDefinedLoader();
refreshLudcl = false;
}
return classCache.get(name, cachedLudcl);
return classByNameCache.get(name, cachedLudcl);
}
} catch (ClassNotFoundException ex) {
Class<?> cl = primClasses.get(name);
Expand Down

0 comments on commit 9915781

Please sign in to comment.