Click here to Skip to main content
Click here to Skip to main content

jQuery Memory Leak in UpdatePanel

By , 20 Mar 2009
 

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:

<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:

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):

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:

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:

(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)

About the Author

gstolarov
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralHi! Please help me.. thank you.memberYU-TIEN25 Aug '09 - 23:20 
Hi :
 
For me I used MS AJAX $addHandler($get('i1'), 'change', add); to regist event at i1 textbox.
I used your code cannot fix UpdatePanel issue.
 
Do you have any idea?
 
Thank you.
 
My Source code:
    Sys.Application.add_load(function() {
            $addHandler($get('i1'), 'change', add);
            $addHandler($get('i2'), 'change', add);
 
//            $('.num').change(add).Disposable();
    });

GeneralRe: Hi! Please help me.. thank you.membergstolarov26 Aug '09 - 3:53 
I'm attaching to the dispose() function also used by Ajax.NET. If your control already have other extenders attached, you might have a problem. Please see "this breaks custom server control" thread below for additional discussion. I personally stopped using Ajax.NET and currently using jQuery Based Ajax.Net library[^] instead so I don't have any conflicts.
Another thing to note that if you have multiple update panels, you might end up attaching events twice if you using the flow you described. Currently in my code I switched to following logic:
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function(mgr, args) {
  $(".shtsFired",GetUpdatePanels(args)).Disposable().change(function() { ... });
});
where
function GetUpdatePanels(args) {
var ret = (args && args.get_panelsUpdated) ? args.get_panelsUpdated() : null;
if (!ret || ret.length == 0) return $(document);
return $(ret);
}
GeneralMy vote of 1memberkeertiraj5559 Apr '09 - 3:35 
integrate jQuery library with Microsoft AJAX.NET---- too many sites available for this
GeneralRe: My vote of 1membergstolarov9 Apr '09 - 15:35 
Wow, you are at the busy start - just joined a CodeProject today and already voted 1 on 7 articles - what a way to encourage people!!! At least I don't feel lonely or offended - apparently I'm in the good company - all the votes for the best articles of the month.
 
But not to be personal... Can you show me another solution that addresses a jQuery memory leak in UpdatePanel? If not, may be you should change your vote.
 
Either way I would be more careful judging other people work - they put some genuine effort into it and trying to share the knowledge among the rest of us who appreciate it.
GeneralRe: [SORRY] My vote of 1memberkeertiraj55511 Apr '09 - 0:14 
I'm really sorry, i've made a big mistake. Rose | [Rose] 4 from me
 
gstolarov wrote:
Wow, you are at the busy start - just joined a CodeProject today and already voted 1 on 7 articles - what a way to encourage people!!! At least I don't feel lonely or offended - apparently I'm in the good company - all the votes for the best articles of the month.But not to be personal... Can you show me another solution that addresses a jQuery memory leak in UpdatePanel? If not, may be you should change your vote. Either way I would be more careful judging other people work - they put some genuine effort into it and trying to share the knowledge among the rest of us who appreciate it.

Generalthis breaks custom server control [modified]memberterrytsay20 Mar '09 - 20:59 
I have a custom server control ( renders a textbox ) and in which i have jquery events attached to the same textbox outside the server control script.
However, once I added this plugin to address the memory leak issue, I started getting
 
"two components with the same id {0} can't be added to the application".
 
After some debugging, I think that by attaching a dispose to the textbox, MS Ajax's dispose method on the client stopped getting invoked when updatepanel posts back, which causes the textbox not unregistered with scriptmanager. And once updatepanel updates the html and register the textbox with scriptmanager, the exception is thrown.
 
How to address this? I can prevent this from happening by simply comment out el.dispose = cleanup; but it defeats the original purpose.
 
appreciate your work on this becuase I have been looking for this memory leak solution for days and couldnt find any until seeing your post today and immediately tried it out and it worked great exception for the server/ajax control issue above.
 
---- EDIT ----
I actually found a workaround .. which is not good but so far it works.

function cleanup() {
if (!el)
return;
$(el).unbind();
$(window).unbind("unload", cleanup);
if(Sys.Application){
var obj = $find(el.id);
// manually unregister the textbox from scriptmanager
if(obj)
Sys.Application.removeComponent(obj);
}
// only kill the dispose function if it is what we attached
if(el.dispose == cleanup) {
el.dispose = null;
el = null;
}
};
 


 
modified on Saturday, March 21, 2009 3:57 AM

GeneralRe: this breaks custom server controlmembergstolarov21 Mar '09 - 5:37 
Thanks for a great suggestion.
You are correct - Ajax CTP also uses dispose method. I didn't check Ajax CTP but the plugin doesn't attach itself to the dispose if something is already there. Which means it's not predictable which dispose method you are going to get - this plugin or Ajax.CTP - whichever source code would end up be included first.
 
Two points though:
1- What would be a need to to extend same object with Ajax CTP and JQuery? If you are attaching events use one or another. And if you just using jQuery to traverse DOM - no reason to dispose - I think.
2- Try my library (the one mentioned in the code - http://www.codeproject.com/KB/ajax/jqueryajax.aspx[^]). I had some issues with extender controls and completely stopped using CTP. That is why I didn't catch this issue.
GeneralRe: this breaks custom server controlmemberterrytsay23 Mar '09 - 9:28 
In regard to your question:
This is the scenario in my case:
I develop custom server controls that renders textbox and attach some events with behavior.
I also use jquery to do Application Wide events
ie. Attach blur/focus event to ALL textbox/Textarea/Select so textboxes change color on focus
ie. Attach blur/focus event to ALL textbox/Textarea so on blur it changes all input to upper case
 
So you see that my server control's textbox overlap with other Application Wide jquery events and the server control was developed using CTP's methodology so it works with UpdatePanels and attach scripts / object initializer .. etc.
 
So in my case, the Application Wide scripts will get executed first - its where I use the Disposable plugin - meaning the dispose method will be attached. Then when CTP object gets created, it sees a dispose method and will not attach its own - causing the unregistration with script manager to fail.
GeneralAlternative to using plugin if using jQuery 1.3 or highermemberTheYo20 Mar '09 - 13:15 
Using jQuery 1.3 you could also work around the issue by using live events.
 
jQuery will then auto attach the handler to the objects whenever they are added to the page (because during the ajax call the dom objects are recreated)
 
ex:
    $(document).ready(function() { 
	$('.num').live('change',add); 
    }); 

GeneralRe: Alternative to using plugin if using jQuery 1.3 or highermembergstolarov20 Mar '09 - 13:51 
Thanks - I didn't know about live() option. However it still would not unbind an object when UpdatePanel is about to dispose it's contents and as such should still leak memory. I will however test it to make sure.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 20 Mar 2009
Article Copyright 2009 by gstolarov
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid