Skip to content

Commit 908999b

Browse files
committed
[JENKINS-63766] Work around JDK-8231454
1 parent 8732e31 commit 908999b

File tree

1 file changed

+40
-0
lines changed

1 file changed

+40
-0
lines changed

src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import hudson.model.Action;
5353
import hudson.model.Result;
5454
import hudson.util.Iterators;
55+
import io.jenkins.lib.versionnumber.JavaSpecificationVersion;
5556
import jenkins.model.CauseOfInterruption;
5657
import jenkins.model.Jenkins;
5758
import org.jboss.marshalling.Unmarshaller;
@@ -1298,6 +1299,7 @@ private static void cleanUpLoader(ClassLoader loader, Set<ClassLoader> encounter
12981299
if (encounteredClasses.add(clazz)) {
12991300
LOGGER.log(Level.FINER, "found {0}", clazz.getName());
13001301
Introspector.flushFromCaches(clazz);
1302+
cleanUpClassInfoCache(clazz);
13011303
cleanUpGlobalClassSet(clazz);
13021304
cleanUpClassHelperCache(clazz);
13031305
cleanUpObjectStreamClassCaches(clazz);
@@ -1358,6 +1360,44 @@ private static void cleanUpGlobalClassValue(@NonNull ClassLoader loader) throws
13581360
}
13591361
}
13601362

1363+
private static void cleanUpClassInfoCache(Class<?> clazz) {
1364+
JavaSpecificationVersion current = JavaSpecificationVersion.forCurrentJVM();
1365+
if (current.isNewerThan(new JavaSpecificationVersion("1.8"))
1366+
&& current.isOlderThan(new JavaSpecificationVersion("17"))) {
1367+
try {
1368+
// TODO Work around JDK-8231454.
1369+
Class<?> classInfoC = Class.forName("com.sun.beans.introspect.ClassInfo");
1370+
Field cacheF = classInfoC.getDeclaredField("CACHE");
1371+
try {
1372+
cacheF.setAccessible(true);
1373+
} catch (RuntimeException e) { // TOOD Java 9+ InaccessibleObjectException
1374+
/*
1375+
* Not running with "--add-opens java.desktop/com.sun.beans.introspect=ALL-UNNAMED".
1376+
* Until core adds this to its --add-opens configuration, and until that core
1377+
* change is widely adopted, avoid unnecessary log spam and return early.
1378+
*/
1379+
if (LOGGER.isLoggable(Level.FINER)) {
1380+
LOGGER.log(Level.FINER, "Failed to clean up " + clazz.getName() + " from ClassInfo#CACHE. A metaspace leak may have occurred.", e);
1381+
}
1382+
return;
1383+
}
1384+
Object cache = cacheF.get(null);
1385+
Class<?> cacheC = Class.forName("com.sun.beans.util.Cache");
1386+
if (LOGGER.isLoggable(Level.FINER)) {
1387+
LOGGER.log(Level.FINER, "Cleaning up " + clazz.getName() + " from ClassInfo#CACHE.");
1388+
}
1389+
Method removeM = cacheC.getMethod("remove", Object.class);
1390+
removeM.invoke(cache, clazz);
1391+
} catch (ReflectiveOperationException e) {
1392+
/*
1393+
* Should never happen, but if it does, ensure the failure is isolated to this
1394+
* method and does not prevent other cleanup logic from executing.
1395+
*/
1396+
LOGGER.log(Level.WARNING, "Failed to clean up " + clazz.getName() + " from ClassInfo#CACHE. A metaspace leak may have occurred.", e);
1397+
}
1398+
}
1399+
}
1400+
13611401
private static void cleanUpGlobalClassSet(@NonNull Class<?> clazz) throws Exception {
13621402
Class<?> classInfoC = Class.forName("org.codehaus.groovy.reflection.ClassInfo"); // or just ClassInfo.class, but unclear whether this will always be there
13631403
Field globalClassSetF = classInfoC.getDeclaredField("globalClassSet");

0 commit comments

Comments
 (0)