|
UnderstandingMemoryLeaks
DomEventsAndMemoryLeaks is an older article with a bit more background on the underlying leak-prevention strategy. There are two basic kinds of memory leaks:
Java-esque Leaks You can actually analyze the first case using standard Java tools in dev mode. They won't correctly tell you the amount of memory leaked, but they will tell you which objects are leaked, which is often quite illuminating. The actual amount of memory leaked in the browser should be proportional to the amount leaked in the JVM for "normal" objects; but for native/dom objects it can be quite different. Again, it's important to note that these aren't actually "leaks" in the C/malloc sense of the word -- they'll eventually get cleaned up, but could hang around longer than you want them to. IE Leaks Now we get to IE leaks (I refer to them as IE leaks because they're a very specific variety that only happen in IE, though to be fair they could occur in antediluvian versions of Mozilla). These are leaks that follow a very specific pattern (detailed in the references below) involving a circular reference between a Javascript object and a native COM object (DOM elements, XHR, XML elements, et al). To be very clear, no other circular ref will do -- it must involve one object of each type (again, see the references below for details). As covered in detail on the GWT wiki (see below), the core GWT widget system has a very specific event-handling system that makes it impossible to trigger an IE leak, as long as you don't go straight to JSNI and hook up event handlers yourself (using things like Event.sinkEvents() is just fine). The same goes for built-ins like XHR/RequestBuilder, Timer, Window events, and the like. "Leaks" on Other Browsers Any remotely recent version of WebKit (Chrome, Safari, et al) or Gecko (Firefox) does not leak memory in the same way that IE does. They can, however, exhibit slightly different behavior when cleaning up circular references between native and Javascript objects. I can't speak to precisely what Firefox does, but Chrome's generational garbage collector won't collect objects involved in these references until it does a "major compaction". This happens less frequently than early-generation collections, so you may find native objects hanging around a bit longer than you'd expect. Observing Memory Usage First, let's start with the assumption that you're looking at memory in Task Manager (or Process Explorer). What you'll typically see is that, even in a reasonably-behaved web app, memory will increase to a point, then stabilize. If you're refreshing the app, you'll also typically see the memory appear to increase slowly over time -- but if you do it long enough, it will eventually drop back down, usually to a higher point than where it started, but stable. There's also a subtly confusing behavior when you minimize the browser window -- memory will drop to a very low level, then increase rapidly once you restore the window. The important thing to keep in mind here is that memory usage naturally fluctuates, and watching it increase over a single refresh is not sufficient to prove a leak. In a true leak situation, memory usage will increase without bound, and it takes some time to prove that. IE8 Caveat Observing memory usage on IE8 is a bit trickier. It appears to be allocating memory for a given instance of a page in a pool, which it then dumps on unload. This means that all of an app's memory will be freed when you close or refresh it. So if you want to find leaks on IE8, you need to watch its usage while it's running, which can take some time. Exacerbating Factors Ok, so why am I seeing gobs of memory wasted on IE? Often it's the case that your app isn't actually leaking memory on IE, but simply consuming a lot more than on other browsers. The most common cause I've run into has been DirectX filters, usually when trying to hack CSS opacity support. Whenever you see a CSS property that starts with "filter:", whether it be "filter:opacity(...)" or "filter:progid:DXImageTransform..." and so forth, you're using a DirectX filter. These little beasts can use a lot of memory, because they all seem to allocate an additional offscreen buffer for every element they're applied to. My advice? Get rid of them. Accept that your app won't be as pretty in IE6/7 and move on. I once saw an app that blew ~1.5G of memory on IE7 (yes, that's gigabytes) that wasn't leaking -- it just had an enormous number of opacity filters. Getting rid of them dropped it down to a much more reasonable level. Things That Might Help
native void hookSomething(JavaScriptObject something, Callback cb) /*-{
something.onfoo = function() {
cb.@my.Callback::onfoo();
};
}-*/;
Things That Almost Certainly Won't Help
IE Memory Leak References:
IE Memory Leak Tools:
|
The part about "Exacerbating Factors" is not 100% correct.
Sure "filter:opacity(...)" or "filter:progid:DXImageTransform..." will use a lot of memory in IE6/7 but .. in IE7 there is no need to use them! There are bugs reported on this - soft permutations were promised to handle this. Also don't forget that if you run in quircksmode on IE8, that it also falls back on IE6/7 behavior.
The following GWT code leaks at a rate of 500KB per-second on I.E 8/9 running on Vista or Windows 7. Seams to not be a problem on I.E. 7 on windows XP After running for 2 minutes it is now consuming 280MB of memory and still climbing.
public class TestApp implements EntryPoint { VerticalPanel centerPanel = new VerticalPanel(); Timer refreshTimer = new Timer() { public void run() { updateGUI(); schedule(1000); } }; public void onModuleLoad() { RootPanel.get().add(centerPanel); refreshTimer.schedule(1000); } public void updateGUI(){ centerPanel.clear(); String theDate = new Date().toString(); Grid grid = new Grid(43, 5); for(int i = 0; i < 43; i++){ grid.setWidget(i, 0, new Label("test1")); grid.setWidget(i, 1, new Label("test2")); grid.setWidget(i, 2, new Label("test3")); grid.setWidget(i, 3, new Label("test4")); grid.setWidget(i, 4, new Label(theDate)); } centerPanel.add(grid); } }This is all very fascinating, but I've been using GWT for my company's main client for almost four years now, and I can categorically say that GWT leaks, despite the best efforts of the programmer. It's far more egregious in applications that constantly and continuously create, add and remove DOM elements, especially complex ones like dialogs. (Even without any JSNI or non-standard event handling). It's also more obvious when a single application window stays active for an entire 8-hour shift or longer, rather than navigating continuously among component pages. See http://code.google.com/p/google-web-toolkit/issues/detail?id=6938 (and above commenter) for a very clear example of stock GWT leaking like crazy in modern IE. How can you say that GWT "Doesn't leak" with evidence like this?