Architecture
How DIXY sees a deadlock.
Java App → Java agent / JVMTI → JFR and event collection → event processing → wait-for graph → deadlock detection → diagnostic output.
Target JVM
Collection
Processing
Detection
Output
Cycle detection
DeadlockDetectorAvailableDepth-first search for cycles. PinningDetector keeps cycles containing a PINS edge.
Component interaction
Detection happens inside your JVM.
The agent keeps its own wait-for graph in the target process, so a cycle is found as soon as the last edge appears. The backend stores telemetry and reports for the CLI and API.
In the target JVM
- VirtualDeadlockAgent, collectors, EventSender
- GraphGateway + WaitForGraph + DeadlockDetector
- RemoteReportSink (no Spring, no Jackson — fits the slim agent jar)
In the DIXY application
- REST controllers with X-API-Key security (port 8081)
- PostgreSQL persistence (Flyway) and Redis
- Spring Shell commands and the dixy detect scanner
Event pipeline
One event, five steps.
- 01
Collect
JVMTI callbacks, the JFR stream and the thread-state poller each turn JVM activity into an EventDTO.
- 02
Send
EventSender hands every event to the in-process GraphGateway, then posts it to /api/telemetry without blocking.
- 03
Apply
GraphGateway updates owner and waiter registries under a ReentrantLock — a lock that parks, so it creates no monitor events of its own.
- 04
Detect
DeadlockDetector runs a DFS on the WaitForGraph after each update. A cycle becomes a DeadlockCycle.
- 05
Report
RemoteReportSink posts the cycle to /api/internal/deadlocks, with a 30-second cooldown per cycle to avoid duplicates.
Wait-for graph
A worked example.
Thread T1 holds lock A and wants lock B. Thread T2 holds lock B and wants lock A.
- thread
- lock
- holds
- waits
- deadlock cycle
// T1synchronized (a) { synchronized (b) { … } // waits: T2 holds b} // T2synchronized (b) { synchronized (a) { … } // waits: T1 holds a}Collapsing the lock nodes gives the thread-only view the detector walks: T1 → T2 → T1. Any path that returns to its start is a deadlock.
Detection workflow
Watch a deadlock form, step by step.
Use Play, the arrows, or the step markers. With reduced motion enabled, Play jumps to the final step.
- thread
- lock
- holds
- waits
- deadlock cycle
Step 1 / 6
Two threads, two locks
T1 and T2 are running. Nobody owns lock A or lock B yet. The graph has nodes but no edges.
JVM integration notes
Details that keep reports honest.
JFR JavaMonitorEnter is history
JFR emits it after the monitor is acquired, so DIXY treats it as "wait is over" instead of a live edge. This removed false cycles.
Waits without an owner are ignored
A blocked event with no known owner (e.g. Object.wait) cannot be part of a cycle, so it adds no edge.
No self-monitoring by default
Attaching JVMTI hooks to DIXY's own JVM caused a recursive feedback loop; the agent targets a separate JVM.
Platform coverage
The native library is bundled for Linux x86-64 and Windows x86-64. macOS and ARM need a custom build.
More in the docs: JVM Diagnostics · Deadlock Detection