Ezer éve nem volt módom (és időm) írni, de most sikerült, megtaláltam, és megengedek magamnak ekkora pihenőt. Jó ideje egy GWT alapú alkalmazás készülget, és eddig szinte minden „simán” ment a fejlesztés során, volt néhány kisebb trükk, pár gwt-s osztályt át kellett írni, de összességében szerintem a GWT keretrendszer jól teljesített, sőt felül is múlta az elvárásaimat.
Egészen tegnapig, amikor is azt jelezték, hogy az alkalmazás sok memóriát fogyaszt. Ilyenkor az ember hajlamos azt mondani, hogy persze, hiszen nagy alkalmazás, de a C++-hoz szokott énem rögtön kétségbe esett, ez bizony memory leak. Gyors mérések után a tünetek egyértelműek voltak. Az alkalmazás leak-el, menüpontonként pár megát. Az látszott, hogy nem a javascript memóriahasználata nő (google chrome kiírja), hanem a böngésző memória használata. Ekkor kezdtem gyanakodni, hogy ezt pedig mi rontottuk el, és a végén igazolódott is ez az állítás. A hibát csak a tanúsága miatt megosztom mindenkivel.
Az eredeti kód:
private void setLoadingVisible(boolean pVisible) {
if (pVisible) {
waitingPopup=new APopupWaiting(msgs.main_waitingmessage(), Window.getClientWidth(),Window.getClientHeight());
waitingPopup.center();
}
if (!pVisible && waitingPopup.isVisible()) {
waitingPopup.hide();
}
Ez a szolgáltatás arra szolgál, hogy ha tölteni kell, akkor kirak egy töltés dialógust. Első látásra minden rendben van, létrehoz egy újat, megjeleníti, vagy elrejti a megadott paraméter alapján. Az implementáció hibás, az már érdekesebb kérdés miért. Normál esetben maximum egy nem optimális implementáció lenne, hiszen miért hozunk mindig létre valamit, ami már egyszer létre lett hozva, ráadásul sohasem változik. Sokkal nagyobb probléma, hogy ez javascriptben fut, és a GWT a változót (és a hozzá javascript által generált HTML dom-ot) csak akkor szabadítja fel, ha már nincs rá referencia. Ellenben a PopupDialogra igen is lesz referencia, és nem a DOM-on keresztül, hanem azon eseménykezelők miatt, amikre működéséből fakadóan feliratkozott. Tehát az objektum lóg a levegőben, és fogyasztja a memóriát.
Egy lehetséges megoldás:
private void setLoadingVisible(boolean pVisible) {
if (pVisible) {
if (waitingPopup==null) {
waitingPopup=new APopupWaiting(msgs.main_waitingmessage(),
Window.getClientWidth(),Window.getClientHeight());
waitingPopup.center();
} else {
waitingPopup.center();
}
}
if (!pVisible && waitingPopup!=null) {
waitingPopup.hide();
}
}
Végezetül egy kis olvasmány (de az ellen nem véd ;):
http://code.google.com/p/google-web-toolkit/wiki/DomEventsAndMemoryLeaks
http://blog.performize-it.com/2008/06/client-side-memory-leaks-ie-and-gwt.html