Skip to content

Commit 4c114e9

Browse files
committed
Merge pull request #543 from basil/JENKINS-63766
[JENKINS-63766] Work around JDK-8231454 (cherry picked from commit 0449852)
1 parent 624c3d6 commit 4c114e9

File tree

1 file changed

+50
-7
lines changed

1 file changed

+50
-7
lines changed

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

Lines changed: 50 additions & 7 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;
@@ -1316,6 +1317,7 @@ private static void cleanUpLoader(ClassLoader loader, Set<ClassLoader> encounter
13161317
if (encounteredClasses.add(clazz)) {
13171318
LOGGER.log(Level.FINER, "found {0}", clazz.getName());
13181319
Introspector.flushFromCaches(clazz);
1320+
cleanUpClassInfoCache(clazz);
13191321
cleanUpGlobalClassSet(clazz);
13201322
cleanUpClassHelperCache(clazz);
13211323
cleanUpObjectStreamClassCaches(clazz);
@@ -1376,6 +1378,44 @@ private static void cleanUpGlobalClassValue(@NonNull ClassLoader loader) throws
13761378
}
13771379
}
13781380

1381+
private static void cleanUpClassInfoCache(Class<?> clazz) {
1382+
JavaSpecificationVersion current = JavaSpecificationVersion.forCurrentJVM();
1383+
if (current.isNewerThan(new JavaSpecificationVersion("1.8"))
1384+
&& current.isOlderThan(new JavaSpecificationVersion("16"))) {
1385+
try {
1386+
// TODO Work around JDK-8231454.
1387+
Class<?> classInfoC = Class.forName("com.sun.beans.introspect.ClassInfo");
1388+
Field cacheF = classInfoC.getDeclaredField("CACHE");
1389+
try {
1390+
cacheF.setAccessible(true);
1391+
} catch (RuntimeException e) { // TODO Java 9+ InaccessibleObjectException
1392+
/*
1393+
* Not running with "--add-opens java.desktop/com.sun.beans.introspect=ALL-UNNAMED".
1394+
* Until core adds this to its --add-opens configuration, and until that core
1395+
* change is widely adopted, avoid unnecessary log spam and return early.
1396+
*/
1397+
if (LOGGER.isLoggable(Level.FINER)) {
1398+
LOGGER.log(Level.FINER, "Failed to clean up " + clazz.getName() + " from ClassInfo#CACHE. A metaspace leak may have occurred.", e);
1399+
}
1400+
return;
1401+
}
1402+
Object cache = cacheF.get(null);
1403+
Class<?> cacheC = Class.forName("com.sun.beans.util.Cache");
1404+
if (LOGGER.isLoggable(Level.FINER)) {
1405+
LOGGER.log(Level.FINER, "Cleaning up " + clazz.getName() + " from ClassInfo#CACHE.");
1406+
}
1407+
Method removeM = cacheC.getMethod("remove", Object.class);
1408+
removeM.invoke(cache, clazz);
1409+
} catch (ReflectiveOperationException e) {
1410+
/*
1411+
* Should never happen, but if it does, ensure the failure is isolated to this
1412+
* method and does not prevent other cleanup logic from executing.
1413+
*/
1414+
LOGGER.log(Level.WARNING, "Failed to clean up " + clazz.getName() + " from ClassInfo#CACHE. A metaspace leak may have occurred.", e);
1415+
}
1416+
}
1417+
}
1418+
13791419
private static void cleanUpGlobalClassSet(@NonNull Class<?> clazz) throws Exception {
13801420
Class<?> classInfoC = Class.forName("org.codehaus.groovy.reflection.ClassInfo"); // or just ClassInfo.class, but unclear whether this will always be there
13811421
Field globalClassSetF = classInfoC.getDeclaredField("globalClassSet");
@@ -1423,13 +1463,16 @@ private static void cleanUpObjectStreamClassCaches(@NonNull Class<?> clazz) thro
14231463
for (String cacheFName : new String[] {"localDescs", "reflectors"}) {
14241464
Field cacheF = cachesC.getDeclaredField(cacheFName);
14251465
cacheF.setAccessible(true);
1426-
ConcurrentMap<Reference<Class<?>>, ?> cache = (ConcurrentMap) cacheF.get(null);
1427-
Iterator<? extends Entry<Reference<Class<?>>, ?>> iterator = cache.entrySet().iterator();
1428-
while (iterator.hasNext()) {
1429-
if (iterator.next().getKey().get() == clazz) {
1430-
iterator.remove();
1431-
LOGGER.log(Level.FINER, "cleaning up {0} from ObjectStreamClass.Caches.{1}", new Object[] {clazz.getName(), cacheFName});
1432-
break;
1466+
Object cache = cacheF.get(null);
1467+
if (cache instanceof ConcurrentMap) {
1468+
// Prior to JDK-8277072
1469+
Iterator<? extends Entry<Reference<Class<?>>, ?>> iterator = ((ConcurrentMap) cache).entrySet().iterator();
1470+
while (iterator.hasNext()) {
1471+
if (iterator.next().getKey().get() == clazz) {
1472+
iterator.remove();
1473+
LOGGER.log(Level.FINER, "cleaning up {0} from ObjectStreamClass.Caches.{1}", new Object[]{clazz.getName(), cacheFName});
1474+
break;
1475+
}
14331476
}
14341477
}
14351478
}

0 commit comments

Comments
 (0)