...
Description of problem: We are evaluating OpenJDK 17 early access builds for a part of our product. We see missing class unload events received by Eclipse over JDI, when running a dedicated test to ensure class unload events are sent and received. Upon investigation, we can reproduce with a plain Java snippet and jdb. The problem is also seen with OpenJDK 16, but not with OpenJDK 15 (or, OpenJDK 11 and OpenJDK 8). Version-Release number of selected component (if applicable): OpenJDK 16 OpenJDK 17 Steps to Reproduce: First, I've made a change to OpenJDK code to check whether all class unload events are being sent (in case the Eclipse JDI implementation broke for some reason with OpenJDK 17). Its a printf in eventHandler.c : 1. Apply the following patch on top of either OpenJDK 16 or OpenJDK 17 source code (e.g. from https://github.com/openjdk/jdk17.git) and compile the JDK (this allows seeing which class unload events will be sent over JDI): diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c index 2a5912f0777..19a91126e18 100644 — a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c @@ -500,6 +500,7 @@ synthesizeUnloadEvent(void *signatureVoid, void *envVoid) char *durableSignature = jvmtiAllocate((int)strlen(signature)+1); (void)strcpy(durableSignature, signature); + printf("Sending class unload event for: %s\n", durableSignature); eventHelper_recordClassUnload(node->handlerID, durableSignature, eventBag); 2. Place the following Java snippet in some folder: import java.io.File; import java.net.URL; import java.net.URLClassLoader; public class TestClassUnloadEvents { public static void main(String[] args) throws Exception { new TestClassUnloadEvents().execute(); } private URLClassLoader urlClassLoader; public void execute() throws Exception { beforeClassLoading(); loadClasses(); afterClassLoading(); unloadClasses(); afterClassUnloading(); } private void loadClasses() throws Exception { int numberOfClassesToLoad = Integer.valueOf("10"); String libraryPath = "classes.jar"; URL[] urls = { new File(libraryPath).toURI().toURL() } ; urlClassLoader = new URLClassLoader(urls, null); for (int i = 0; i < numberOfClassesToLoad; ++i) { String className = "sample.C" + i; Class<?> loadClass = urlClassLoader.loadClass(className); loadClass.newInstance(); } } private void unloadClasses() throws Exception { urlClassLoader.close(); urlClassLoader = null; System.gc(); System.runFinalization(); } public static void beforeClassLoading() { System.out.println("BEFORE CLASS LOADING"); } public static void afterClassLoading() { System.out.println("AFTER CLASS LOADING"); } public static void afterClassUnloading() { System.out.println("AFTER CLASS UNLOADING"); } } 3. Place the attached "classes.jar" jar file in the same folder as the snippet. 4. From that folder run the snippet. E.g.: /data/git/jdk17/build/linux-x86_64-server-release/jdk/bin/java -Xlog:class+load,class+unload -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8080 TestClassUnloadEvents.java 5. Attach jdb with: /data/git/jdk17/build/linux-x86_64-server-release/jdk/bin/jdb -attach 8080 6. Input "resume" for JDB, to let the application finish. 7. Repeat the previous steps, observe that some prints (if not all) for class unloads are often missing. Actual results: Example output that I (often) see, which is unexpected: ... AFTER CLASS LOADING [4.863s][info][class,load] java.io.RandomAccessFile$1 source: /data/git/jdk17/build/linux-x86_64-server-release/jdk/modules/java.base [4.867s][info][class,unload] unloading class sample.C9 0x00000008001c5c30 [4.867s][info][class,unload] unloading class sample.C8 0x00000008001c5a28 [4.867s][info][class,unload] unloading class sample.C7 0x00000008001c5820 [4.867s][info][class,unload] unloading class sample.C6 0x00000008001c5618 [4.867s][info][class,unload] unloading class sample.C5 0x00000008001c5410 [4.867s][info][class,unload] unloading class sample.C4 0x00000008001c5208 [4.867s][info][class,unload] unloading class sample.C3 0x00000008001c5000 [4.867s][info][class,unload] unloading class sample.C2 0x00000008001c4c10 [4.867s][info][class,unload] unloading class sample.C1 0x00000008001c4a08 [4.867s][info][class,unload] unloading class sample.C0 0x00000008001c4800 [4.876s][info][class,load ] java.lang.ref.Finalizer$2 source: /data/git/jdk17/build/linux-x86_64-server-release/jdk/modules/java.base Sending class unload event for: Lsample/C9; Sending class unload event for: Lsample/C7; Sending class unload event for: Lsample/C5; Sending class unload event for: Lsample/C3; [4.876s][info][class,load ] java.lang.ref.Finalizer$1 source: /data/git/jdk17/build/linux-x86_64-server-release/jdk/modules/java.base AFTER CLASS UNLOADING ... Expected results: Expected output that I sometimes see (with Java 11 I always see all expected prints from the change, where event order is not relevant): ... AFTER CLASS LOADING [6.312s][info][class,load] java.io.RandomAccessFile$1 source: /data/git/jdk17/build/linux-x86_64-server-release/jdk/modules/java.base [6.315s][info][class,unload] unloading class sample.C9 0x00000008001c5c30 [6.315s][info][class,unload] unloading class sample.C8 0x00000008001c5a28 [6.315s][info][class,unload] unloading class sample.C7 0x00000008001c5820 [6.315s][info][class,unload] unloading class sample.C6 0x00000008001c5618 [6.315s][info][class,unload] unloading class sample.C5 0x00000008001c5410 [6.315s][info][class,unload] unloading class sample.C4 0x00000008001c5208 [6.315s][info][class,unload] unloading class sample.C3 0x00000008001c5000 [6.315s][info][class,unload] unloading class sample.C2 0x00000008001c4c10 [6.315s][info][class,unload] unloading class sample.C1 0x00000008001c4a08 [6.315s][info][class,unload] unloading class sample.C0 0x00000008001c4800 [6.325s][info][class,load ] java.lang.ref.Finalizer$2 source: /data/git/jdk17/build/linux-x86_64-server-release/jdk/modules/java.base Sending class unload event for: Lsample/C9; Sending class unload event for: Lsample/C7; Sending class unload event for: Lsample/C5; Sending class unload event for: Lsample/C3; Sending class unload event for: Lsample/C1; Sending class unload event for: Lsample/C0; Sending class unload event for: Lsample/C8; Sending class unload event for: Lsample/C6; Sending class unload event for: Lsample/C4; Sending class unload event for: Lsample/C2; [6.325s][info][class,load ] java.lang.ref.Finalizer$1 source: /data/git/jdk17/build/linux-x86_64-server-release/jdk/modules/java.base AFTER CLASS UNLOADING ... Additional info: Using our test, I've bisected the bug to those 2 commits: commit ba721f5f2fbbf08e22a9993dce0087f46b1f5552 (HEAD -> bad) Author: Coleen Phillimore <coleenp@openjdk.org> Date: Thu Nov 19 14:30:02 2020 +0000 8212879: Make JVMTI TagMap table concurrent Co-authored-by: Kim Barrett <kbarrett@openjdk.org> Co-authored-by: Coleen Phillimore <coleenp@openjdk.org> Reviewed-by: stefank, ihse, zgu, eosterlund, sspitsyn, kbarrett commit 3a4b90f0863571dd372af6afab71862f3946a78f (good) Author: Sean Mullan <mullan@openjdk.org> Date: Thu Nov 19 14:15:57 2020 +0000 8202343: Disable TLS 1.0 and 1.1 Reviewed-by: xuelei, dfuchs, coffeys With HEAD at 3a4b90f0863571dd372af6afab71862f3946a78f, the problem is not seen. With HEAD at ba721f5f2fbbf08e22a9993dce0087f46b1f5552, the problem is seen. So I assume the cause is: https://github.com/openjdk/jdk17/commit/ba721f5f2fbbf08e22a9993dce0087f46b1f5552
Won't Do