The current mechanism for generating JDWP CLASS_UNLOAD events is somewhat twisted, and leads to a couple of bugs. JVMTI has no (public) ClassUnload event support. It does for some reason in the extension mechanism, but it is not used. In order to determine when a class unloads, the debug agent tags (as in JVMTI SetTag) all Class instances. This results in the ObjectFree() callback being called each time a Class instance is collected.
The ObjectFree() callbacks are not done by the GC. After GC is done (and after the GarbageCollectionFinish event has been posted), a Service Thread task collects all the tagged objects that have been freed. It then calls the following:
// PostObjectFree can't be called by JavaThread, so call it from the VM thread.
void JvmtiTagMap::post_dead_objects_on_vm_thread() {
VM_JvmtiPostObjectFree op(this);
VMThread::execute(&op);
}
So the posting of the ObjectFree events is deferred to the VM Thread, which unlike the Service Thread is not a Java Thread. Because it is not a JavaThread, JDWP events likely cannot be sent from it (so no immediately sending of the CLASS_UNLOAD event). It’s not clear why they can’t be sent, and possibly they can. More on below when discussing solutions.
Since the CLASS_UNLOAD events cannot be delivered from the VM Thread, the classes are instead queued up in a list called deletedSignatures. When JVMTI sends the GarbageCollectionFinish event (also from the VM Thread), the debug agent bumps up the garbageCollected counter. This counter is checked in event_callback(), which is called for any event received on a Java thread. At that point the deletedSignatures list is processed, and a CLASS_UNLOAD event is sent for each (and garbageCollected is cleared).
There are two bugs that arise from this deferred handling of CLASS_UNLOAD events:
JDK-8256811 Delayed jdwp class unloading events
JDK-8257705 vmTestbase/nsk/jdi/HiddenClass/events/events001.java timed out
JDK-8256811 is due to the fact that the GarbageCollectionFinish event comes in before any ObjectFree events. This means that while ObjectFree events are being generated (and before all of them have been generated), it’s possible for some other JVMTI event to come in and trigger the garbageCollected check in event_callback(), which will result in processing the deletedSignatures list prematurely. After this is done more ObjectFree events come in and are added to the list, but they will not get processed until there is another GC, which might not be for a while.
JDK-8257705 is also related to the garbageCollected check. Simply put, once there is a GarbageCollectionFinish event, deletedSignatures is not processed until an event comes in on a Java thread. This potentially may not happen for a while, and theoretically may never happen. It depends of what types of EventRequests the debugger has currently setup, and what the application does to trigger them. The test is doing nothing to ensure there is another event after it triggers the class unloading. It seems usually one does come in sporadically. It’s hard to say for sure what that event is, but is could be a CLASS_PREPARE triggered by some VM Java thread. In any case, the event is not reliably generated, so the test sometimes fails waiting for CLASS_UNLOAD event(s).
[NOTE: The solutions suggested below are no longer being suggested for the reason given in the second comment below. Instead the suggestion in the first comment seems to be a reasonable choice to fix these issues.]
As for a solution, what would fix this is being able to generate the CLASS_UNLOAD event immediately from the thread that the ObjectFree event was delivered on. So this currently would mean the VM Thread, but also potentially JVMTI could be changed to make it the Service Thread. Each potential has a problem.
For the VM Thread, the concern is that traditionally the debug agent has avoided sending CLASS_UNLOAD from the VM Thread. The reason is not clear. CLASS_UNLOAD is the only event that has this issue of the debug agent wanting to deliver it from the VM Thread. Unlike most other events, the event does not include the event thread, which is a good thing because we wouldn’t want the debugger being told the event occurred on the VM Thread. However, the EventRequest can request that the event thread be suspended, or all threads be suspended. If event thread suspension was chosen, the debugger can’t possibly know which thread that is, so it will have no choice but to use VM.resume() to resume it, which is the same thing it would do if all threads were suspended. Since the debugger can’t possibly know which thread the event occurred on, the debug agent may be able to get away with suspending the VM Thread as long as it gets resumed by VM.resume().
For the Service Thread case, it would be nice if rather than deferring to the VM Thread, the CLASS_UNLOAD events were just immediately delivered on the Service Thread. This presumably simplifies the above solution in that the event thread will be suspendable. There are a few concerns thought.
(1) JDK-8212879 moved the collecting of all tagged objects that were freed to the Service Thread, but Coleen said when she first did this, there was an issue posting the ObjectFree event from the Service Thread, so she kept the collecting on the Service Thread, but deferred the processing to the VM Thread by introducing the above mentioned JvmtiTagMap::post_dead_objects_on_vm_thread() method. It’s unclear what this issue is with posting directly from the Service Thread, but seemed to have been related to needing to do a vm state transition. Coleen suggested trying again and seeing.
(2) Suspending the Service Thread might not be such a good idea. In that case it would have to remain unsuspected much like the VM Thread in the first solution, and therefore subject to the same concerns expressed there.
(3) Although it is a Java Thread, the Service Thread is not a thread that the debug agent tracks because it is not returned by GetAllThreads. This makes it a challenge to suspend during event processing like other threads normally are. So suspended or not, we are back to having to special case it in various parts of the debug agent.
I’m inclined to suggest first trying the VM Thread solution, and not suspending it. We know the posting of ObjectFree on the VM Thread works (that’s how it is done now), so the question is can we get the debug agent to also generate the CLASS_UNLOAD event, and will there be any issues with not suspending the “event thread”.