It is always the same scenario: the support team contacts us with a problem of a "frozen GUI". They send us the logs so that we can investigate, but of course the logs do not show anything. The user already restarted its GUI, so when we ask the support to perform a jstack, it is already too late. Quite often, it is not even due to a deadlock, but to a long operation that should not be done in the EDT. Since the only thing we have to investigate is the logs, I decided to write an EDT Freeze Detector that would log any operation monopolizing the EDT for more than 10 seconds. Here is the code:
import java.awt.AWTEvent;import java.awt.EventQueue;import java.awt.Toolkit;import java.util.Timer;import java.util.TimerTask;public class FreezeDetector extends EventQueue{ private static final long FREEZE_TIMER_PERIOD = 10000L; private volatile AWTEvent currentEvent; private volatile Thread eventDispatchThread; private FreezeDetector() { Timer timer = new Timer("Freeze Detector", true); timer.schedule(new FreezeTimerTask(), FREEZE_TIMER_PERIOD, FREEZE_TIMER_PERIOD); } public static void installFreezeDetector() { Toolkit.getDefaultToolkit().getSystemEventQueue().push(new FreezeDetector()); } @Override protected void dispatchEvent(AWTEvent event) { eventDispatchThread = Thread.currentThread(); currentEvent = event; try { super.dispatchEvent(event); } finally { currentEvent = null; } } private class FreezeTimerTask extends TimerTask { private AWTEvent lastEvent; @Override public void run() { if (lastEvent != null && lastEvent == currentEvent) { printStack(); } lastEvent = currentEvent; } private void printStack() { StackTraceElement[] stackTrace = eventDispatchThread.getStackTrace(); StringBuilder sb = new StringBuilder(); sb.append("Freeze detected on EDT:"); for (StackTraceElement stackElement : stackTrace) { sb.append(stackElement.toString()).append('\n'); } //use your favorite logger System.out.println(sb); } } }
Any feedback is welcome.