I noticed the following when debugging a test timeout:
"tid": "3",
"time": "2025-07-10T22:18:05.358610205Z",
"name": "main",
"state": "WAITING",
"waitingOn": "java.lang.Thread@74302b48",
"stack": [
"java.base\/java.lang.Object.wait0(Native Method)",
"java.base\/java.lang.Object.wait(Object.java:389)",
"java.base\/java.lang.Thread.join(Thread.java:1887)",
"java.base\/java.lang.Thread.join(Thread.java:1963)",
"com.sun.javatest.regtest.agent.MainWrapper.main(MainWrapper.java:85)"
],
"monitorsOwned": [
{
"depth": 2,
"locks": [
"java.lang.Thread@74302b48"
]
}
]
},
According to the dump we own the monitor lock for the thread@74302b48, but we are doing a `wait()` and so the monitor has actually been released!
Similarly for TIMED_WAIT
"tid": "24",
"time": "2025-07-10T22:18:05.359142499Z",
"name": "Common-Cleaner",
"state": "TIMED_WAITING",
"stack": [
"java.base\/java.lang.Object.wait0(Native Method)",
"java.base\/java.lang.Object.wait(Object.java:389)",
"java.base\/java.lang.ref.ReferenceQueue.remove0(ReferenceQueue.java:123)",
"java.base\/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:201)",
"java.base\/jdk.internal.ref.CleanerImpl.run(CleanerImpl.java:146)",
"java.base\/java.lang.Thread.run(Thread.java:1474)",
"java.base\/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:148)"
],
"monitorsOwned": [
{
"depth": 3,
"locks": [
"java.lang.ref.ReferenceQueue$Lock@41f73267"
]
}
]
},
but also note that in this case we are missing the "waitingOn" line!
"tid": "3",
"time": "2025-07-10T22:18:05.358610205Z",
"name": "main",
"state": "WAITING",
"waitingOn": "java.lang.Thread@74302b48",
"stack": [
"java.base\/java.lang.Object.wait0(Native Method)",
"java.base\/java.lang.Object.wait(Object.java:389)",
"java.base\/java.lang.Thread.join(Thread.java:1887)",
"java.base\/java.lang.Thread.join(Thread.java:1963)",
"com.sun.javatest.regtest.agent.MainWrapper.main(MainWrapper.java:85)"
],
"monitorsOwned": [
{
"depth": 2,
"locks": [
"java.lang.Thread@74302b48"
]
}
]
},
According to the dump we own the monitor lock for the thread@74302b48, but we are doing a `wait()` and so the monitor has actually been released!
Similarly for TIMED_WAIT
"tid": "24",
"time": "2025-07-10T22:18:05.359142499Z",
"name": "Common-Cleaner",
"state": "TIMED_WAITING",
"stack": [
"java.base\/java.lang.Object.wait0(Native Method)",
"java.base\/java.lang.Object.wait(Object.java:389)",
"java.base\/java.lang.ref.ReferenceQueue.remove0(ReferenceQueue.java:123)",
"java.base\/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:201)",
"java.base\/jdk.internal.ref.CleanerImpl.run(CleanerImpl.java:146)",
"java.base\/java.lang.Thread.run(Thread.java:1474)",
"java.base\/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:148)"
],
"monitorsOwned": [
{
"depth": 3,
"locks": [
"java.lang.ref.ReferenceQueue$Lock@41f73267"
]
}
]
},
but also note that in this case we are missing the "waitingOn" line!