Skip to content

Commit

Permalink
Merge pull request #543 from basil/JENKINS-63766
Browse files Browse the repository at this point in the history
[JENKINS-63766] Work around JDK-8231454
  • Loading branch information
jglick authored May 24, 2022
2 parents 8732e31 + 5934d66 commit 0449852
Showing 1 changed file with 50 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import hudson.model.Action;
import hudson.model.Result;
import hudson.util.Iterators;
import io.jenkins.lib.versionnumber.JavaSpecificationVersion;
import jenkins.model.CauseOfInterruption;
import jenkins.model.Jenkins;
import org.jboss.marshalling.Unmarshaller;
Expand Down Expand Up @@ -1298,6 +1299,7 @@ private static void cleanUpLoader(ClassLoader loader, Set<ClassLoader> encounter
if (encounteredClasses.add(clazz)) {
LOGGER.log(Level.FINER, "found {0}", clazz.getName());
Introspector.flushFromCaches(clazz);
cleanUpClassInfoCache(clazz);
cleanUpGlobalClassSet(clazz);
cleanUpClassHelperCache(clazz);
cleanUpObjectStreamClassCaches(clazz);
Expand Down Expand Up @@ -1358,6 +1360,44 @@ private static void cleanUpGlobalClassValue(@NonNull ClassLoader loader) throws
}
}

private static void cleanUpClassInfoCache(Class<?> clazz) {
JavaSpecificationVersion current = JavaSpecificationVersion.forCurrentJVM();
if (current.isNewerThan(new JavaSpecificationVersion("1.8"))
&& current.isOlderThan(new JavaSpecificationVersion("16"))) {
try {
// TODO Work around JDK-8231454.
Class<?> classInfoC = Class.forName("com.sun.beans.introspect.ClassInfo");
Field cacheF = classInfoC.getDeclaredField("CACHE");
try {
cacheF.setAccessible(true);
} catch (RuntimeException e) { // TODO Java 9+ InaccessibleObjectException
/*
* Not running with "--add-opens java.desktop/com.sun.beans.introspect=ALL-UNNAMED".
* Until core adds this to its --add-opens configuration, and until that core
* change is widely adopted, avoid unnecessary log spam and return early.
*/
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.log(Level.FINER, "Failed to clean up " + clazz.getName() + " from ClassInfo#CACHE. A metaspace leak may have occurred.", e);
}
return;
}
Object cache = cacheF.get(null);
Class<?> cacheC = Class.forName("com.sun.beans.util.Cache");
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.log(Level.FINER, "Cleaning up " + clazz.getName() + " from ClassInfo#CACHE.");
}
Method removeM = cacheC.getMethod("remove", Object.class);
removeM.invoke(cache, clazz);
} catch (ReflectiveOperationException e) {
/*
* Should never happen, but if it does, ensure the failure is isolated to this
* method and does not prevent other cleanup logic from executing.
*/
LOGGER.log(Level.WARNING, "Failed to clean up " + clazz.getName() + " from ClassInfo#CACHE. A metaspace leak may have occurred.", e);
}
}
}

private static void cleanUpGlobalClassSet(@NonNull Class<?> clazz) throws Exception {
Class<?> classInfoC = Class.forName("org.codehaus.groovy.reflection.ClassInfo"); // or just ClassInfo.class, but unclear whether this will always be there
Field globalClassSetF = classInfoC.getDeclaredField("globalClassSet");
Expand Down Expand Up @@ -1405,13 +1445,16 @@ private static void cleanUpObjectStreamClassCaches(@NonNull Class<?> clazz) thro
for (String cacheFName : new String[] {"localDescs", "reflectors"}) {
Field cacheF = cachesC.getDeclaredField(cacheFName);
cacheF.setAccessible(true);
ConcurrentMap<Reference<Class<?>>, ?> cache = (ConcurrentMap) cacheF.get(null);
Iterator<? extends Entry<Reference<Class<?>>, ?>> iterator = cache.entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getKey().get() == clazz) {
iterator.remove();
LOGGER.log(Level.FINER, "cleaning up {0} from ObjectStreamClass.Caches.{1}", new Object[] {clazz.getName(), cacheFName});
break;
Object cache = cacheF.get(null);
if (cache instanceof ConcurrentMap) {
// Prior to JDK-8277072
Iterator<? extends Entry<Reference<Class<?>>, ?>> iterator = ((ConcurrentMap) cache).entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getKey().get() == clazz) {
iterator.remove();
LOGGER.log(Level.FINER, "cleaning up {0} from ObjectStreamClass.Caches.{1}", new Object[]{clazz.getName(), cacheFName});
break;
}
}
}
}
Expand Down

0 comments on commit 0449852

Please sign in to comment.