K napsání tohoto článku mě inspiroval Ondra
Medek svými maily v javovské konferenci v lednu 2010,
v nichž se podivoval nad tím, že Java automaticky
neuklízí zavřená okna. Pokud okno nemá nastaveno
DISPOSE_ON_CLOSE
, je při zavření pouze
schováno a nadále zabírá paměť. K jeho dealokaci
dojde až při zavolání metody dispose()
. V tomto článku si ukážeme, jak lze pomocí AspectJ
sledovat okna v našem programu.
Okno vzniká voláním konstruktoru a zaniká voláním
metody dispose()
. Využijeme toho, že
AspectJ umožňuje připojit advice před i za
volání konstruktoru a metody.
Pointcut pro konstruktor třídy Window
a libovolného potomka definujeme
takto (konstruktor může mít libovolné parametry):
pointcut newWindow():
call(Window+.new(..));
Pointcut pro metodu dispose()
ve třídě Window
a
libovolném potomkovi zapíšeme následovně:
pointcut disposeWindow(): call(public void
Window+.dispose());
Bezprostředně po provedení konstruktoru si uložíme
hashCode
okna spolu s informacemi, kde
(číslo řádku) a jak (signatura konstruktoru) bylo
okno vytvořeno. Tyto informace nám pomůžou, až se
budeme snažit vysledovat okno, které nebylo
dealokováno. Pro ukládání informací použijeme dva
seznamy: seznam windows
pro hashCode
a seznam info
pro
dodatečné informace o okně.
after() returning(Window w): newWindow() { int h = w.hashCode(); windows.add(h); String s = String.format("%d: %s, %s", h, thisJoinPoint.getSourceLocation(), thisJoinPoint.getSignature()); info.add(s); }
Při dealokaci (dříve než dojde k zavolání dispose()
) informace o zavíraném okně ze
seznamů odstraníme.
before(): disposeWindow() { int h = thisJoinPoint.getTarget().hashCode(); int i = windows.indexOf(h); windows.remove(i); info.remove(i); }
Dále přidáme výpis seznamu oken v pravidelných
intervalech a na konci metody main
:
pointcut main(): execution(public static void main(String[])); before(): main() { Integer i = Integer.getInteger("interval"); if (i != null) { new Timer(true).schedule( new TimerTask() { public void run() { printLiveWindows(); } }, i, i ); } Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() { public void run() { printLiveWindows(); } } ) ); }
Celý aspect je k dispozici zde. Chcete-li jej použít ve svojí aplikaci,
nainstalujte si AspectJ
a aspect i zdrojáky přeložte překladačem
ajc
. Např. takto:
ajc -1.6 monitoring/WindowAspect.aj app/*.java
Aspect bude fungovat jen ve třídách,
které s ním byly přeloženy. Necháte-li tedy vytváření
oken na nějakém frameworku, aspect toto
nezachytí. Funguje to zhruba tak, že při překladu
pomocí ajc
se přidá za každé volání
konstruktoru a před každé volání dispose()
volání našeho kódu.
Pokud si chcete aspect jen vyzkoušet,
stáhněte si ukázkovou aplikaci (testapp.jar
) a běhovou podporu pro
AspectJ (aspectjrt.jar
). Aplikaci pustíte
příkazem
java -jar testapp.jar
Chcete-li vypisovat informace o oknech průběžně,
použijte příkaz
java -Dinterval=20000 -jar testapp.jar
Časový údaj je v milisekundách. Soubor aspectjrt.jar
musí být v aktuálním adresáři.