Monday, November 17, 2008

IE7 + removing DIV containing SWFObject dynamically = Memory leak

But don’t worry, there is a fix.

I’m currently working on a project that makes use of the MSN custom player, which is basically a flash widget that shows MSN video on a third party site. I wanted to load this flash widget dynamically when someone clicks on a ‘launch video player’ link on a page (basically displaying the widget in a lights-out effect by using a modal). I also display a ‘close video player’ link that closes the widget and fades out the modal.

On clicking the ‘launch video player’ link a JavaScript command is used to load the player into an existing DIV on the page by dynamically attaching a Flash SWFObject which contains the widget. And clicking on the ‘close video player’ hides the DIV containing the player (based on DOM rules, by hiding this parent DIV, the Flash SWFObject should be disposed from the DOM and the browser should reclaim the memory)

The problem

As also reported in this article titled ‘IE7 memory is not released after removing DIV containing SWFObject from DOM’, the problem is with IE7 (and possible other IE versions) not releasing the Flash SWFObject from memory. It seems that when you use DOM commands to remove a parent DIV of a SWFObject, the reference to it still remains in memory (even on page reloads). This leads to a pretty serious memory leak and IE eventually crashing under the accumulated memory load.

The Fix

I got the solution to this problem from reading this article titled ‘IE Triggers Memory Leak in Flash’; the solution is to delete all scripts and functions within the SWFObject embed and then deleting the actual SWFObject itself.

This function does just that:
function removeFlaskLeakInIE(id) {
    var obj = document.getElementById(id);
        if (obj) {
            for (var i in obj) {
                if (typeof obj[i] == "function") {
                obj[i] = null;
                }
        }
        obj.parentNode.removeChild(obj);
    }
}

Call it in IE by sending in the id of the Flash SWFObject:

if (jQuery.browser.msie) {
        removeFlaskLeakInIE('video_layer_flash_obj');
}

Note: Calling the above function in browsers other than IE may cause an error like this ‘Trying to add unsupported property on scriptable plugin object!’, this is basically because some browsers cant reference child object using [i]. But since this is only an IE issue we don’t need to make this code cross-browser.

I confirmed this problem was fixed by

1) Using the good old ‘Task Manager’ to inspect the memory being used before and after I closed the player.

2) The IE memory leak tool Drip also confirmed this by showing a leak in the 'video_layer_flash_obj’ object before implementing the fix and then showing that the leak stoppped after the fix

Feels good to fix issues like this as they are architecture related and make a big difference in speeding up your applications :)

4 comments:

  1. Your solution didn't help with IE7 :(.. It removes object (mp3 player), but swf keeps loading the file (i watch traffic). Only page reloading helps.. I don't know what to do..

    ReplyDelete
  2. Thanks very much for your fix. I was doing something *very* similar, performed all my primary debugging in FF, turned to IE and was having issues.

    Much appreciated!

    Dan

    ReplyDelete
  3. I was facing the memory leak problems in IE and i getting confuse about your scripting part.Can i ask how i need to do for call out the id.Can you explain more details.Thanks

    ReplyDelete
  4. Hi Anonymous,

    It’s actually pretty simple. You flash embed object should have an id (just like other html elements like Div, Span). You use this id with JavaScript to point to your HTML object or in this case the flash embed object and set its style etc.

    If you flash embed object does not have an id, you need to give it one.
    e.g.
    <...embed id="movie_player"...>

    Now just include my script code for the removeFlaskLeakInIE function and call it like this:

    if (jQuery.browser.msie) {
    removeFlaskLeakInIE('movie_player');
    }

    The jQuery.browser.msie is a Jquery function that returns true if you browser is IE, if you don’t want to use that just code your own check for IE browser.

    ReplyDelete

Fork me on GitHub