Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / XHTML

jQuery Memory Leak in UpdatePanel

4.74/5 (24 votes)
20 Mar 2009CPOL4 min read 88.6K  
Addressing jQuery memory leak in UpdatePanel

Introduction

This article discusses how to integrate jQuery library with Microsoft AJAX.NET, in particular with <asp:UpdatePanel/> and to avoid Internet Explorer memory leak while doing so.

jQuery becomes more and more popular. However Microsoft ASP.NET and AJAX.NET provide their own framework of communicating with the server. jQuery AJAX mechanism seems clumsy and un-natural in ASP.NET environment. It's much easier to use ASP.NET UpdatePanels for AJAX functionality and use jQuery for their selectors functionality - pretty much what I understood Microsoft is planning to do. However doing so is tricky and this article explains at least some of the shortfalls of doing so.

Iteration 1 - Sample Page

Let's imagine a sample calc - page that adds 2 numbers:

ASP.NET
<body> 
    <form id="form1" runat="server"> 
   	<asp:ScriptManager runat="server" ScriptMode=Debug> 
   	    <Scripts> 
		<asp:ScriptReference Path="~/jquery-1.2.6.debug.js" /> 
	    </Scripts> 
	</asp:ScriptManager> 
	<asp:UpdatePanel ID='up' runat="server"><ContentTemplate> 
	    <asp:TextBox ID='i1' runat="server" CssClass='num' Width='50px'/>+ 
	    <asp:TextBox ID='i2' runat="server" CssClass='num' Width='50px'/>= 
	    <asp:TextBox ID='res' runat="server" Width='50px'/> 
	    <asp:Button ID='btn' runat="server" Text='...'/> 
	</ContentTemplate></asp:UpdatePanel> 
    </form> 
</body> 
<script> 
	function add() { 
		$get('res').value = parseInt($get('i1').value) 
			+ parseInt($get('i2').value); 
	} 
	$(document).ready(function() { 
		$('.num').change(add); 
	}); 
</script> 

This page should not require much explanation if you have some familiarity with ASP.NET and jQuery:

  • We include ScriptManager and jquery script.
  • Standard form
  • We define 3 input controls, first two of which have class='num' - so we can easily find them with jQuery
  • Contents are placed in <asp:UpdatePanel> and button is provided to refresh the panel
  • Function is defined to get 2 numbers, add them up and store the result in the third input box
  • Standard jQuery mechanism is provided to attach change event so when one of the numbers gets changed, calculations are performed.

The resulting page looks like this:

Image 1

The stuff initially seems to work. You can change the numbers and see the result on the screen. However try to refresh the UpdatePanel - and suddenly calculations break.

Explanation

The reason for failure after the UpdatePanel refresh is that the contents of the UpdatePanel go away along with all the events attached to them.
To solve the problem, we need to use AJAX.NET provided way of attaching events to the page (here I'm attaching only script portion):

JavaScript
function add() {
	$get('res').value = 
		parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
	$('.num').change(add);
});

Iteration 2 - Memory Leak

The approach suggested in iteration 1 worked for me for a while. Except eventually, I started to notice that Internet Explorer slows down. A lot. And then looking at the process monitor, I noticed that Internet Explorer would leak memory on the moderate size form like a megabyte per UpdatePanel refresh. Looking at the sIEve tool (God bless the heart of the creators), I saw that the input elements are not been destroyed:

Image 2

You can see that every time I click refresh, it creates a new set of input elements without destroying the previous ones.

Explanation

Googling it for a while I noticed that Internet Explorer memory leak is a sore subject for a lot of developers. Most of the complaints were identified as either pseudo leak (memory is actually released but you need a complete page refresh to see it) or as closures.

The closure is an interesting topic. It refers to the JavaScript event attached to the DOM element. Apparently Internet Explorer uses 2 garbage collectors - one for DOM and another for JavaScript. There are some semantics involved but the bottom line is that if you attached an event to the DOM object, then neither garbage collector would be able to get rid of the involved objects/DOM elements. I strongly encourage you to read more about closures on the WEB.

Well the jQuery is all about closures. They are nice enough to attach to window.unload and cleanup when the page gets unloaded, but they have no idea regarding UpdatePanel. Something needs to be done to cleanup jQuery when UpdatePanel is about to be refreshed.

Iteration 3 - jQuery Cleanup Plugin

UpdatePanel does provide cleanup hooks. When it's about to throw an element away, it checks to see if there is a element.dispose function. If such a function exists, then it will be executed.

So I ended up creating the following jQuery plugin to take care of the situation:

JavaScript
(function($) {
	$.fn.Disposable = function(cln) {
		return this.each(function() {
			var el = this;
			if (!el.dispose) {
				el.dispose = cleanup; // will be called by 
						    // Microsoft for cleanup
				$(window).bind("unload", cleanup);
			}
			function cleanup() {
				if (!el)
					return;
				$(el).unbind();
				$(window).unbind("unload", cleanup);
				el.dispose = null;
				el = null;
			};
		});
	};
})(jQuery);
function add() {
	$get('res').value = 
		parseInt($get('i1').value) + parseInt($get('i2').value);
}
Sys.Application.add_load(function() {
	$('.num').change(add).Disposable();
});

As you can see, this jQuery plugin attaches a cleanup() function to element.dispose. Whenever element.dispose is being called, all the jQuery events will be unbound from the element and allow garbage collector to reclaim all the objects.

Summary 

The code I'm discussing is actually part of another library I posted a while ago at jQuery Based Ajax.Net library. You are welcome to download that project and browse the code at your leisure.

History

  • 20th March, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)