Tuesday, July 25th, 2006
Biggest AJAX problem
Spend any time with any language out there (Javascript, PHP, ASP, etc) and you’ll run into problems. Most of these can be resolved with a few tweaks here and there, but sometimes, things can get the best of you. Ajax code isn’t immune either, and with the addition of an outside content source, things can get pretty hectic pretty quickly. Remus Stratulat found this out firsthand when he had to track down a memory leak in his code - resulting in Firefox swallowing up 700MB of memory.
AJAX is becoming the new “de facto” standard in the web application industry. If you ask a JavaScript developer about what is the biggest problem of JavaScript programming it will probably tell you it’s browsers’ incompatibilities. In my opinion there is a worse problem then hacking your way around bugs and differences. That problem is represented by memory leaks.
He notes that, most of the time, Javascript can clean up after itself, freeing up variables and resources for other scripts to use. Unfortunately, Ajax causes a chink in the page’s armor - a way to let content in that could sometimes destroy the rest of the page (or just start clogging up the RAM on the machine until it grinds to a halt).
Checking your code is always a must, of course, but is there a way that problems like this can be handled at a more basic level?












Just checking a plain stupid idea. Supposed that you have an Ajax framework that degrades. That is - that works with and without JavaScript enabled in a pretty similar way (just that the whole page is reloaded when JS is off).
In order to clear the allocated memory, would it be “good enough” to just “load the full page” once per 10 clicks? It’s not the most well architected solution, but this could really clean up the whole mess, without forcing the developer to manually remove memory pointers, unregister programatically add event listeners, and so on …
What do you think?
Alexandru
(we’re working on an degradable Ajax platform, check the demo here (with or without JS on))
http://www.interaktonline.com/demos/ajax/
Doesn’t Google’s GWT solve the cross-browser problem
cleanly?
Right now almost all AJAX frameworks and libraries are hiding the cross-browser incompatibilities from the developer.
No Alexandru, nothing will replace sound programming practices and resource allocation/de-allocation. Reloading the whole page every (pick an arbitrary number) clicks or actions would make for quite an inconsistent and frustrating user experience, not to mention make it much more difficult for the developer who has to deal with application and component level state (do you keep state on the client sometimes and then marshal it to the server for these total reloads, or do you send it to the server with every state changing action?). Truely scalable ajax apps will have to incorporate custom garbage collection to handle the cases of DOM fragment overwrites and circular references.
Can any of you share some of the techniques you used to track down memory problems? I agree 100% that memory leaks not browser incompatibilities are what should (or already do) keep ajax developers up at night.. But the tools to identify and fix these problems are few and far between, and those that exist are not very sharp. I’m aware of Drip, and there was a similar tool for FF as I recall? What else is out there? Tools / testing and troubleshooting methodologies?
My experience has been that its largely a matter of poring over code, finding DOM nodes references that might not be removed properly, checking event handlers are removed.. but its long, laborious work with no guarantee at the end of it that you really found the important problems, or that you fixed them.
The AJAX frameworks will have to provide the developer with the tools to allow him to handle this problem. The basics are there, if you add a listener you have the method to remove it. When you create DOM objects you must be able to destroy them.
I just hope the debugging tools continue to get better and better. I honestly don’t know what I’d do without the DOM Inspector and Firebug…
I use FireBug but that is not for leaks detection. Soon more capable tools must emerge or AJAX development will remain a guessing business regarding this problem.
Could someone please let the readers of this site know why you refuse to fix the commenting system? Really, what is the reason?
Probably to discourage the spamming engines at they will receive an error.
Sam, the best way to prevent leaks is look at your architecture as a whole. The best “tool” is good application design from the bottom up. If you are poring through files that are filled with markup, server code, and javascript throughout, looking for all the individual places where there might be a circular reference or potential leak, then yes, you can never really be sure you got them all. If your design is clean, with good separation of concerns, and controls are componentized then memory management will fall right in.
The moral of the story here is not “Javascript developers aren’t doing a good job of garbage collection”, it’s “Browser developers aren’t doing a godo job of garbage collection”.
There’s no malloc in JS, it’s supposed to be a gc language. These are interpreter bugs that JS coders are having to work around.
One of the biggest problems I see is that there is no (reasonable) way to removeEventListeners in JavaScript.
Imagine some DOM elements that have listeners attached. Then, the DOM elements are removed, but the events remain attached…
I had this kind of problems when implementing a simple tooltip engine, where after removing the target elements from the DOM, the tooltip elements were still there, and no GC engine would remove those…
So at the end, it’s a question of good programming and a good GC.
Bogdan
Hi Ryan,
I know about sound programming practices, but I’m in the web development business (selling tools), and I can assure you that there are very few developers and senior developers there, while there are millions of newbies that want to do Ajax and that are actually deploying live websites
As for the reloading experience, we’ve analyzed in depth the Gmail experience when doing Ajax navigation. While we suppose only data in exchanged with the server, the page refresh process is the following:
Change cursor to “waiting”Show Red Loading Label in the upper rightShow white pageShow new page
I presume that the experience for the user was ok as actually nobody saw this :)
And for managing the states, we’re trying to solve the application state problem in our framework, solving this developer problems automatically. But indeed, it was a very, very difficult problem to solve.
Let’s just wait and see how good web developers/newbies will get in learning advanced programming techniques, or whether the browsers will get better in improving the JS GC
Alexandru
Yes, JavaScript is a GC language. And yes, some times the browser GC or JSVM does not do a good job in cleaning the memory. I found a reference about it in this article: http://www.bazon.net/mishoo/home.epl?NEWS_ID=1281
However, the job of a JavaScript programmer has become more difficult in the AJAX era and that is the focus of my article. In the old days a JavaScript programmer was comfortable enough in being sloppy with the objects he was creating as the browser was cleaning everything up when the page was reloaded. When you are developing a single page AJAX application everything is loaded and created in the same scope. If the programmer does not take care to “null†everything that he has created when he no longer needs it then you have a big problem. Not to mention about unregistering all the listeners from all the events. It is very easy to get lost in the referencing scheme. So unfortunately the JavaScript developers aren’t doing a good job of garbage collection.
There are 8 millions html developers and each one of them will think sooner or later to get his hand dirty with some AJAX. They will pull some content from the server and create a bunch of objects and register events to them. After that they will pull some more content from server and do the same thing on the same page. What do you think has happened with the objects he first created? They will continue to linger in a “no objects land†inside the JSVM, ghosts… memory leaks.
@Peter: Even very good garbage collected languages will have cases where resources are being accessed outside of the managed environment, in which case there is no alternative other than the developer writing custom finalizers (dispose methods) on their objects, and ensuring that they get called when appropraite. For js UI components this amounts to setting DOM element references and event handler references to null. One approach, as I’ve alluded to, is creating a custom AJAX control garbage collector that handles this process in the background. Once that system is built into the application architecture, it makes it slightly easier for the developers; they just write the finalizer methods for each component and the system handles when those methods get called.
@Alexandru: Sorry, wasn’t implying you didn’t know good programming practices :-)… agreed that there are oh-too-many designers/amateurs trying to handle application development with very little concern for architecture. Even though much of the application has potentially migrated to the client, this does not mean we can do away with the developers, it just means the developers need to learn this environment, and let the designers do what they do best, design.
Here at TIBCO in the TIBCO General Interface AJAX RIA toolkit, we addressed the memory issue for AJAX back in 2001. The solution we found was to implement an abstration layer for the native DOM, managed by a client-side controller in a MVC paradigm. Thus adding and removing from the higher level DOM, managed through the controller enabled the controller to manage clean up of objects and memory in a more predictable and manageable way. Keep in mind this technique was used to support Web applications that look and perform like desktop GUIs as opposed to slight enhancements here and there in the context of HTML pages (e.g. Client/SOA ajax as opposed to Enriched HTML page ajax). This “2 DOM” approach as early as 2001 was suffucuent to power chemogenomic drug disovery solutions deployed to the USFDA and major pharma companies. That solutions was very data instensive and loaded/unloaded about 180 various application modules over lengthy (multiple hour) sessions. There’s a recording including discussion about that solution here: http://www2.sys-con.com/webinararchive.cfm?registered=on&pid=61
Forgive me for being a noob, but what exact cases are there where memory isn’t being deallocated properly? Previous comments allude to DOM nodes that are not in the document tree not deallocating when the fall out of scope. Is this really the case? If so, isn’t this primarily an issue with the browser-specific implementations of the JS GC?
In any case, it doesn’t appear that there’s an easy way of tracking such a thing with modern JS debugging tools…
@Kevin: that’s interesting. I’d read about your 2 DOM approach before, but hadn’t really understood it in this context. This is the kind of thing I’m fishing for: real tried-and-tested patterns that address the core problem. The event cache is another - implemented in Dojo and Prototype among others. And higher level widgets are another - which provide a consistent interface which includes a destroy method that can get called automatically or as necessary to re-draw/re-purpose the screen. What else is out there?
My impression for the last year or so has been that a lot of work has gone into getting folks started with ajax, but much of the work that ensures success is still behind closed doors. Is this really critical competitive advantage that is being deliberately withheld? Or is it simply that most of us are still in early development phases and finding this stuff out as we go along.
As we each move from the general to the specific in our development, its understood that a lot of the lessons learned are exclusive to the particular application we’re working on. But many are not. I’m looking for good accounts of the end-game. The postings around the Yahoo Homepage change / @media presentation were valuable in providing candid information on the problems that team faced and how they solved them. Performance and memory management are bugbears that we all face. Are there any more good resources out there that address these issues?
@Kevin: Interesting, I honestly fell by a very similar technique by my own accord (I swear), with DOM referencing and cleanup across incremental partial page updates (via componentized MVC model) abstracted, and a global GC system.
@Sam: The answer is yes and yes. Yes, much of this stuff is considered proprietary to those who have discovered it, packaged it, and are trying to sell it, and yes, it’s all part and parcel to the learning curve of this new frontier. As you move through what is possible, you start running into problems. Problems are nothing more than opportunities to find novel solutions. If you are like some, once you find those novel solutions, you see dollar signs :-). It’s not about hiding things from people on purpose to oppress them though, it’s about timing and opportunity.
@Nick: I am primarily a Java developer. The Java GC does a very good job but even it can not solve all the problems. A good developer or a good architect should not overlook this problem any more.
No matter how good a GC will become there is a boundary that it will not cross and that boundary is cleaning objects that you are using. Defining that boundary is the biggest challenge a VM designer must tackle.
When the browser closes a page the line between used objects and unused ones is simple and clear. When the page resides in a browser for a longer period of time the GC may have a hard time in defining the used and unused references. If the developer does not take the necessary precautions in cleaning after himself the GC will not be able to perform its job.
Hi,
could someone make it clear
1) Do you need to unset events binded with Event.observe() (prototype.js) Once the corresponding piece is refreshed with ajax call?
2) What is cross-browser way to unload iframes? (and check if they’re loaded)
Thanks in advance
Mark,
1) Using prototype as the example, and assuming “this” is an object representing your component/control/widget… notice that you should always create a handle to your event handling functions, so you can properly dispose of them using Event.stopObserving (and then remember to set the handle to null to remove all references). You can dispose of the event handlers and DOM references before you make the AJAX call, which must be a synchronous operation to be reliable, OR you can implement a DOM reference abstraction layer and pass the cleanup process to an asynchronous garbage collector object. The latter is better overall for performance of the app, but the former is easier to implement.
this.someEventHandler = this.doSomething.bindAsEventHandler(this);
Event.observe($('someElement'), "click", this.someEventHandler);
//..and then later in the dispose method, either before the call which would result in this DOM fragment being over-written, or asynchronously via the DOM referencing abstraction/GC layer...
Event.stopObserving($('someElement'), "click", this.someEventHandler);
this.someEventHandler = null;
I actually agree with you on your final point
Ryan,
Thanks alot, i also found an article on that, and almost refined all of my code by the time,
http://www.archivesat.com/Rails_spinoffs/thread392813.htm explains the pattern,
so I’m going to delete references as well, and so far can keep FF memory on level, though IE still have some glitches. (Mainly i think because the way i implemented Google Maps Api)
Thanks for explanation again
Mark, did you notice I wrote that article :-)
Ryan,
hehe, nice job buddy.
I’ll write off on results..
And one more question
If you do like
Event.observe(”element”, “click”, this.myhandler.bindAsEventListener(this));
and then
Event.stopObserving(”element”, “click”, this.myhandler.bindAsEventListener(this));
Does it make a huge difference without saving event handlers and then reseting them?
Thanks again.
[...] The Importance of Maintainable JavaScriptA Basic Approach to Server-side Data Validation with AJAXSafari gets a Javascript debuggerPlanning an Ajax Boot CampLink Thumbnail: Photo Mouse OverBreaking User Interfaces for Fun and ProfitXN Test: The next Unit Testing project?CSS Browser SelectorA Java-based HTTP Proxy for AjaxDeclarative AjaxJson.NET 1.1: Converting between XML and JSONAJAX pagination made simple (with Symfony)Integration of Spry and PHP/MySQLAjax as a Remedy for the Cacheability-Personalization DilemmaJ2EE and AJAX: AJAX with ServletsAdobe Spry and PHP/MySQLGo forth and APITuesday Morning RoundupAjax Activity IndicatorsEcho2 Widget PanelSlightly ThickerBoxJSON.NETJson.NET: Library to help with .NET - JS communicationuniAjax: an ajax framework focused on browser supportAtlas June CTPRelay: Ajax File ManagerDojo Available in Ning ApplicationsIntelliJ IDEA Google Web Toolkit SupportPrivate and Public Members in JavaScriptSpeeding up Prototype’s $$ SelectorNokia and Backbase cooperate on Mobile AJAXFacebook Giving Away Free iTunes MusicOtavo Launches - Yahoo Answers, Friendster and del.ici.ous Rolled into OneMike Potter Builds a Flash-y Ajax SiteStylehive Gets FundingImageKind Launches - CafePress for Wall ArtGPokr: Ajax Poker AppBiggest AJAX problemDabble Searches YouTube, MySpace Video, Metacafe and MoreNextcat - MySpace For EntertainmentDiigo Launches, Nobody CaresSocialtext Open Launches - Commercial Open Source WikiTeamSugar Launches Social Network for WomenFolkd is a Half-Decent Digg Clone (Finally)Gotuit - YouTube for Premium Content?Safari: Browser.Back + AjaxTechnorati Turns Three, Releases Major UpdateAdvanced Box Model TestingXMLHttpRequest Quirks and PHPMySpace Goes OfflineGoogle PaintInterview with Jakob NielsenIs AJAX Accessibility a major issue?Free AJAX Training CourseTwo Key Challenges for Ajax Adoption that We Have IgnoredMODx CMS - An Ajax/PHP Content SystemWeb API authentication for mashupsOPML IconExplaining AJAXPayPerPost: Right or Wrong?Google CheckoutJavaRef: Ajaxified JavaDocInterview with ZK Creator Tom YehLondon Tube Route Finder We posted about memory problems the other day (“the biggest Ajax problem†:-) and while it’s always good practice to clean up, another good practice is to avoid allocating excessive memory in the first place. To that end, I’ve recently been experimenting with the Gang Of Four Flyweight pattern within Javascript. Flyweight works like this. Instead of creating an object for every entity, we only allocate a “Flyweight†object only for every type of entity. Because these Flyweight objects don’t have internal, object-specific, state, we have to pass a little context each time. It’s a compromise to cut down on memory. Flyweight often rears its head when you have some kind of object pooling (e.g. stateless session EJBs). The pattern works well with Ajax apps, where the DOM already holds a lot of context, since we’re displaying that for the user. So I just need to pass in an element (or an element ID) as the context, and the Flyweight object will get the actual state of that element by reading its DOM properties. To use the canonical blog example, we can have two kinds of comments - ManualComments and TrackbackComments. Assuming this is an Alpha blogger who gets 200 ManualComments and 50 TrackbackComments after posting that it’s cloudy outside, we would have a lot of objects to allocate if we did things the naieve way. And, of course, it would get a lot worse with a dynamic app that keeps creating and deleting objects, if there happened to be a memory leak (yes, which should ideally be avoided, but when you have a deadline and one browser or another isn’t playing nice …). Instead of holding 250 Comment objects, we simply use two objects - a ManualComment object and a TrackbackComment object. Each div representing a manual comment would then have a “comment†property referencing the (singleton) ManualComment object. Alternatively, we might prefer to implement some kind of factory that accepts an element and gives us back either the ManualComment object or the TrackbackComment object. So when the user clicks a “Mark As Spam†button inside the div, we have a means of getting back either a ManualComment or a TrackbackComment, and we can then call comment.markAsSpam() on it. We might also have a Comment “superclass†to encapsulate common property (relying on prototype chaining supported by Dojo/Prototype/Base/etc libraries). Unlike Java-style Flyweights, we don’t have to declare abstract methods though; JS being dynamic, we can rely on Duck Typing, so it’s possible that our ManualComment and our TrackbackComment will each have their own markAsSpam() method, but Comment (if it exists at all), will have no such thing. July 25th, 2006 [...]
Mark, sorry so late in replying to that last Q… I don’t think that will work, bud. I believe Event.stopObserving (also meaning the underlying DOM2 event model) requires a handle (or pointer) to the same function as the observe method (same memory address) to work correctly. Remember bind() and bindAsEventListener() return closures (anonymous functions), so you’d be passing in a newly created and different function (meaning different memory address) into the stopObserving method than you did to the observe method. I believe your method would work if you were passing in globally scoped, static function names. As with anything, try it and analyze the results to get an understanding of what’s going on under the covers :-).
hdv2pvs7ish5
I get some bad cases of this. I run a plugin in firefox that restores my session each time I start the browser again. This becomes somewhat like not closing your browsers for weeks on end and when I keep tabs with AJAX apps open in them running that long them memory use is huge!