DE3 - SourceForge



DE3

Powerful AJAX Tools

for Lightweight Portable Web User Interfaces

by

Kou Man Tong Fung Chak Fai

Advised by

Prof. Dekai Wu

Submitted in partial fulfillment

Of the requirement for COMP 396

in the

Department of Computer Science

Hong Kong University of Science and Technology

2006-2007

Name of Student: Signature: Date:

Name of Student: Signature: Date:

Table of Contents

1 Introduction 5

1.1 Overview 5

1.2 Objectives 5

1.3 History 6

1.4 Work Completed 6

2 Object Model and Event System 13

2.1 Introduction 13

2.2 Circular References and Internet Explorer 6 14

2.3 The Lapsed Listener Problem 24

2.4 Performance Implications 29

2.5 The Search for Solutions 39

2.6 Object Model of WT Toolkit 47

2.7 Weak Reference Emulation 51

2.8 Event System 56

3 AJAX RPC System 58

3.1 Using the AJAX RPC System in WT Toolkit 58

3.2 The JSON Data Exchange Format 60

3.3 Using the AJAX RPC System in Web Server 61

4 Regular Widgets System 65

4.1 Introduction 65

4.2 The wtWidget Class 65

4.3 Example Widget Classes 67

5 AJAX Forms System 69

5.1 Introduction 69

5.2 Using AJAX Forms in WT Toolkit 69

5.3 Example Input Form 72

6 Vector Graphical Widgets System 75

6.1 Introduction 75

6.2 Base Components of the Vector Graphics System 76

6.3 Charting Engine 78

6.4 G=(V,E) Graph Engine 82

6.5 3D Objects 83

6.6 Live Demonstrations 83

7 Conclusions 86

8 References 88

9 Appendix A: Source Code for Finding Programming Patterns Prone to Memory Leaks in Internet Explorer 6 91

9.1 Leak Experiments with Simple Circular References 91

9.2 Leak Experiments with Closures 95

9.3 Leak Experiment with Function Objects 98

10 Appendix B: Source Code for Finding JavaScript Operations Affected by Memory Leaks in Internet Explorer 6 100

11 Appendix C: Source Code for Showing JavaScript Object Creation Latency Deteriorating with Number of Reachable JavaScript Objects under Internet Explorer 107

12 Appendix D: Conversations in Dojo Interest Mailing List Regarding the Lapsed Listeners in Dojo Toolkit 111

1. Introduction

1. Overview

Web applications are taking a more and more important part in our daily lives, and they are certainly not limited to convenience applications like Google Maps - the use and development of web applications is penetrating all kinds of modern institutions. For example, enterprise resource planning systems has been implemented as web applications by major ERP vendors since 2000 [14]; web applications are playing a crucial role in the deployment of citywide wireless Internet projects in some cities of the United States [18]. In HKUST, web applications, such as WebCT[12], VELA[11], LMES[24] are playing a more and more important part in teaching activities.

As web applications has progressed from simple HTML tables and forms to modern rich Internet applications [32] that incorporate features and functionalities of desktop applications, there is a great need for robust web application development frameworks that let application developers write web applications with the same kind of ease as developing desktop applications. Recently, a powerful paradigm called AJAX [31] (Asynchronous JavaScript and XML) has emerged in the web development community that makes it possible to write rich, responsive web applications without the use of proprietary browser plug-ins. The AJAX approach was first popularized by Google Maps and Gmail and later adopted by many websites and web applications such as Flickr[5], Google Docs and Spreadsheets[13], EditGrid[26] and PCMS[16]. A significant problem with AJAX is that many people are regarding it as something very difficult and exotic. Some people think that AJAX is so hard that a manager from Microsoft was quoted as saying, “People who do (AJAX development) are rocket scientists” [15]. In this project, we are going to design and implement an open source toolkit, called WT Toolkit, which makes it easy for application developers to write rich AJAX-style web applications.

2. Objectives

The objectives of this project are the following:

1. To make AJAX RPC requests as simple as possible to the eye of a web developer, such that he can have more time to focus on the business logic instead of doing a lot of mechanical coding to handle browser irregularities;

2. To build a rich and robust graphical widget library, such that web developers using WT Toolkit can easily draw convincing user interfaces that give users an impression that what he is using is indeed a serious application, rather than just a web page with forms;

3. To implement a robust event handling mechanism in JavaScript, based on the tried-and-true observer pattern often seen in desktop application development environments;

4. To make WT Toolkit agnostic to the server side architecture.

3. History

WT Toolkit is an open source re-implementation of a proprietary AJAX toolkit developed for the PCMS project (PePWave Centralized Management System) in PePLink Ltd., an R&D oriented company headquartered in Hong Kong. PCMS has been deployed in various city-wide wireless Internet installations in the United States. For example, it is currently deployed in the City of St. Cloud, Florida [18]. It is also mentioned in one of the press releases of Tropos Networks [20], a US company specialized in citywide Wi-Fi Internet service. One of the project team members of this project, Kou Man Tong, is the original developer of both the PCMS system and WT Toolkit.

4. Work Completed

AJAX RPC, Event System, Basic Widget Library

A signal-slot event handling system, a set of DHTML widgets, and the AJAX RPC class (wtRemoteProcedureCall) were implemented in January 2006. These formed the base feature set of WT Toolkit and were being incrementally improved upon during the course of the project.

A real application demo of WT Toolkit, “francium’s Xanga Tracker” [27] was written in January 2006 and has been opened to public registration from February 2006. It has 113 registered users as of 7th April, 2007. Some of the registered users of “francium’s Xanga Tracker” are current Computer Science and Computer Engineering students in HKUST. The earliest versions of “francium’s Xanga Tacker” had a total of less than 10 lines of HTML code – all of the client side user interface logic was implemented in JavaScript with WT Toolkit. The server side logic was implemented in Python. mod_python [9] was used in implementing the server side logic.

[pic]

Figure 1 – Pressing the “Xanga Log” tab triggers a refresh of displayed contents. This operation would leak more than 10MB of memory every time it is executed on the earliest versions of francium’s Xanga Tracker.

It was discovered with the application demo that earlier versions of WT Toolkit had a severe memory leak problem. An early user refreshing his Xanga Log contents in “francium’s Xanga Tracker” for a few times would see the committed memory space of his web browser increase by more than 10MB, for each time he clicked on the “Xanga Log” tab, which triggered the refresh.

Investigations into the memory leak problem showed that two memory management problems existed in the earliest versions of WT Toolkit. One of them was specific to Internet Explorer 6, in which the browser’s garbage collector was unable to determine the liveliness of objects when there exist circular references with DOM objects. Another problem discovered was the lapsed listener problem [25], which is a programming anti-pattern related to primitive implementations of the observer design pattern. It was also found that another popular open source web UI toolkit, Dojo Toolkit [6] suffers from the lapsed listener problem.

To combat the memory management problems in WT Toolkit, weak reference emulation logic was implemented in WT Toolkit. The weak reference emulation logic also provided various side benefits to WT Toolkit in addition to solving the memory leak.

The base features of WT Toolkit described above are reported in details in Sections 2, 3 and 4 of this report.

AJAX Forms, Form Widget Library

One of the most obvious uses for AJAX RPC is to submit user inputs, and get feedback from the web server, without refreshing the web page. An AJAX form class, wtForm, with a set of associated form widgets was implemented in WT Toolkit. The AJAX form system was completed in January 2006 and was being incrementally improved upon during the course of the project.

Similar to the AJAX RPC class, the AJAX form class and all associated form widgets are built upon the toolkit’s signal-slot event handling system. This gives the web developer an extra degree of flexibility in programming input forms – for example, a web developer can use the “ValueChanged” signal of form widgets to notify other JavaScript objects that an input’s value has been changed.

[pic]

Figure 2 - A wtSpreadsheet instance.

Since the AJAX form and the form widget library in WT Toolkit are built upon object oriented principles, sophisticated form widgets can be inherited and aggregated from more basic form widgets in WT Toolkit. For example, the wtSpreadsheet object is made by inheriting from a wtDisplay object and aggregating a number of wtPopupTextbox objects. WT Toolkit is more advanced in this respect when compared to some other popular open source AJAX toolkit like Dojo Toolkit. In Dojo Toolkit, AJAX form is merely a hack to bind AJAX RPC to traditional HTML form DOM nodes [7]. Building new form widgets by aggregation and inheritance in Dojo Toolkit would be much more difficult than in WT Toolkit.

[pic]

Figure 3 - A wtSpreadsheet object is made by inheriting from a wtDisplay object and aggregating multiple wtPopupTextbox objects.

The AJAX form system in WT Toolkit is reported in details in Section 5 of this report.

SVG/VML Vector Graphics

Under the advice of Dr. Dekai Wu, it was discovered in April 2006 that vector graphics can be drawn under Internet Explorer with VML [19] and under Firefox with inline SVG [30]. Initial versions and demonstrations of our SVG/VML module were implemented December 2006, but public release of the code module was only available from March 2007 with the release of WT Toolkit 0.2.1:



Apart from being able to draw basic geometric shapes like lines, polygons, curves, and ellipses. WT Toolkit’s SVG/VML module is capable of drawing tangible widgets aggregated from geometric shapes. For example, WT Toolkit has a wide variety of charting widget classes, such as the bar chart widget class (wtBarChart), the pie chart widget classes (wtPieChart and wtAutoPieChart), the line chart widget class (wtLineChart), the radar chart widget class (wtRadarChart), the scatter chart widget class (wtScatterChart), and many more.

[pic]

Figure 4 - Statistical charts drawn with WT Toolkit's vector graphics system.

Another notable feature of WT Toolkit’s vector graphics system is the ability to draw interactive G=(V,E) graphs. This made it possible for us to write a UML class diagram editor based on WT Toolkit, as one of the application demonstrations of WT Toolkit.

[pic]

Figure 5 - UML class diagram drawn with WT Toolkit's vector graphics system. All UML class diagrams in this report are drawn with this application demo.

The SVG/VML vector graphics system in WT Toolkit is reported in details in Section 6 of this report.

API Documentation

An API documentation describing WT Toolkit’s entire public API was released with WT Toolkit 0.3.0. The documentation was generated with JSDoc [29], a documentation generator very similar to the javadoc tool distributed with Sun Microsystem’s Java Development Kit.

WT Toolkit’s API documentation can also be accessed online, without downloading our code package, by the following URL:



Open Source

WT Toolkit is an open source project. The entirety of our project’s source code are released to be public and licensed under the GNU Lesser General Public License (LGPL) [8].

Our project is hosted on SourceForge:



Packaged code releases of WT Toolkit can be found in the “Download” section of our SourceForge website.

Our development code can be checked out from the SourceForge SVN repository by the command (assuming you have the svn tool installed):

svn co wt-toolkit

2. Object Model and Event System

1. Introduction

The earliest versions of WT Toolkit did not have a comprehensive object model – it was just a collection of code wrapped up in functions to be reused by a programmer. This ad-hoc implementation style had some serious deficiencies, however:

1. It leads to memory leaks, especially under Internet Explorer 6 where the JavaScript garbage collector cannot handle circular references involving DOM nodes.

2. It does not take advantage of built-in OOP features in JavaScript such as prototype inheritance.

3. It made implementing the signal-slot event handling system tricky and prone to errors.

A more comprehensive object model was implemented in WT Toolkit since version 0.2.0 to solve the three problems above. Most of the WT Toolkit code from 0.2.0 and above is encapsulated into classes inherited from the toolkit’s base class, wtObject. The base class provides signal-slot event handling and the ability to act as a proxy to memory expensive (and leak prone) objects like DOM nodes via weak references.

In the subsections that follow from here, section 2.2 explains how memory leaks can occur with circular references in Internet Explorer 6. Section 2.3 explains how memory leaks can occur in naïve signal-slot event handling, or more generally, observer design pattern implementations via the lapsed listener problem [25]. Section 2.4 explains the performance implications to Internet Explorer 6 when there are memory leaks. Section 2.5 explains how the project group searched for solutions for the memory leak problems observed in early versions of WT Toolkit, and how we discovered other popular web UI toolkits to be vulnerable to memory leaks as well. Section 2.6 explains the current object model in WT Toolkit in detail. Section 2.7 explains the weak reference emulation logic, the accompanying automatic garbage collection logic, and a proof of correctness to our garbage collector. Section 2.8 talks about how the event system in WT Toolkit can be used by the web application developer.

2. Circular References and Internet Explorer 6

While Internet Explorer is often criticized for its numerous problems with standard compliance (e.g. poor CSS support) – a less well known, but very significant problem with Internet Explorer 6 is memory leakage. Memory leakage is arguably a more painful problem compared to poor standards compliance. This is because while problems with standard compliance (e.g. a broken CSS attribute) can be easily discovered with test cases, memory leak problems can easily go unnoticed until the very late stages of an application development process.

As discussed in the MSDN article, “Understanding and Solving Internet Explorer Leak Patterns” by Microsoft [22], one of the most common causes for memory leaks under Internet Explorer is circular referencing. The Javascript garbage collector implemented in Internet Explorer uses reference counting for determining the liveliness of an object. As a result, Internet Explorer 6’s garbage collector is unable to determine a set of circular referencing objects to be unreachable in some circumstances, even though the set of objects is no longer used by the web application.

|function createEl(i) { |

|var span = document.createElement("span"); |

|span.className = "muci"; |

|span.innerHTML = " foobar #"+i+" "; |

|span.onclick = function() { alert(this.innerHTML + "\n" + i); }; |

|document.body.appendChild(span); |

|}; |

| |

|function start() { |

|var T1 = (new Date()).getTime(); // DEBUG.PROFILE |

|for (var i = 0; i < 3000; ++i) |

|createEl(i); |

|alert(((new Date()).getTime() - T1) / 1000); // DEBUG.PROFILE |

|}; |

Figure 6 - Bazon's code sample for demonstrating memory leaks in Internet Explorer 6.

Symptoms of memory leaks in Internet Explorer 6 are often encountered by AJAX application developers. An often-cited technical article by Mihai Bazon [2] states the following symptoms:

1. Progressively degrading performance;

2. Unbound growth of memory space occupied by iexplorer.exe;

3. The symptoms of memory leak do not disappear even after a page refresh, or navigating to another URL.

For the purpose of making a web development toolkit, we need to understand how circular references can be formed during application development, and devise solutions to prevent the web developer from too easily forming the above mentioned circular referencing patterns without putting unreasonable burden on the web developer.

Both Microsoft’s and Bazon’s articles did not clearly describe why memory leaks occurred, although they both pointed to circular references as the culprit. For example, in Error! Reference source not found., Bazon claimed that reference cycles are created inside the function createEl(), with two edges in the reference cycle:

1. The variable “span” refers to the closure via its “onclick” attribute;

2. The closure refers back to the variable “span”.

Bazon did not specify why reference number 2 was formed, however; he only specified and demonstrated that reference number 2 exists. In this specific case, it is essential to understand how reference number 2 came into existence even without the identifier “span” being referred to within the closure, such that the appropriate preventive or mitigative measures can be determined for plugging the corresponding memory leak hole. Does the “span” get attached to the closure because Internet Explorer would automatically attach every object reference it can find from the outer scope to a closure? Does the “span” get attached simply because Internet Explorer has determined that the “this” identifier within the closure is identical to “span”, when the closure is assigned to “span” as its onclick attribute? The solution for tackling this memory leak pattern could be different depending on how Internet Explorer decided to attach the outer scope variable’s reference to the closure.

As a result, we conducted some experiments in October 2006 to explore the finer details of memory leaks under Internet Explorer 6.

Methodology of Experiments

While it is possible to determine whether a programming pattern is leaking by looping it a lot of times over and observing whether iexplorer.exe’s committed memory space increases in an unbounded way, even though we are sure that the number of reachable objects in our Javascript program is not increasing; this method is not perfect because it can always be argued that the seemingly unbounded committed memory size growth is observed only because Internet Explorer’s garbage collector has not swept away the unreachable objects, it is no proof to the garbage collector’s inability to determine the unreachability of objects.

[pic]

Figure 7 - It is always possible to argue that the seemingly unbounded allocated memory size growth is only observed because the garbage collector has not yet swept away the unreachable objects, it might do so later. Graph adapted from Chapter 6 of the book “Bitter Java” by Bruce A. Tate. [25]

Luckily, there is a more accurate open source tool for detecting memory leaks in Internet Explorer, called Drip [28]. Drip has an important limitation, however – it cannot detect memory leaks that are not persistent after page refreshes, which are called “pseudo-leak” in Microsoft’s article. This limitation can be deduced by reading Drip’s source code (). By reading the source files MainBrowserDlg.cpp and JSHook.cpp in Drip 0.5’s source tree, it can be deduced that the leak check button of Drip 0.5 works by performing the following procedure:

1. When the “Show DOM Leaks” button is clicked, navigate away from the current URL to “about:blank” to trigger a Javascript garbage collection.

2. When about:blank has completed loading, any DOM objects from the previous page that have not been unloaded is displayed as leaked DOM nodes.

[pic]

Figure 8 - Drip 0.5 is accurate enough to detect a single DOM object being leaked.

It is obvious that DOM objects not “carried over” to the about:blank page could not be detected by Drip as memory leaks.

|void CMainBrowserDlg::OnBnClickedCheckLeaks() { |

|// When the leak test button is pressed, navigate to the blank document |

|// so that the browser will release all of its elements. Set |

|// m_waitingForBlankDoc to true so that the DocumentComplete event |

|// handler will know to check for leaks when the blank document |

|// finishes loading. |

|// |

|requestClosePopups(); |

|Navigate(L"about:blank"); |

|m_waitingForBlankDoc = true; |

|} |

Figure 9 - Code snippet from MainBrowserDlg.cpp of Drip 0.5. This function is the click event handler for the “Show DOM Leaks” button. Notice the function changes the current URL to “about:blank”.

For the purpose of this report, we will still call unbounded memory size growth not persistent after page refreshes as memory leaks, because it is possible and desirable for AJAX applications to operate without any page refreshes at all.

Even with this limitation, Drip is good enough for this part of our investigation, because it has been asserted in both Bazon’s and Microsoft’s articles that memory leaks involving DOM node circular references are persistent even after page refreshes.

With the knowledge that Drip can be used for detecting circular reference memory leaks in Internet Explorer, we can now define an experiment procedure for showing whether a particular programming pattern would leak memory or not:

1. Write a simple JavaScript program with the allegedly leaky programming pattern and embed that JavaScript program in an HTML file.

2. Prepare one or more very similar HTML files, each with only one or two lines of JavaScript essential to the programming pattern modified or removed, for control experiment.

3. Load the HTML file from step 1 to Drip, and see if any memory leak is detected.

4. Load the HTML file from step 2 to Drip, and see if any memory leak is detected.

5. If Drip reports positive result on step 3 and negative result on step 4, then the programming pattern is determined to leak memory in Internet Explorer 6.

Experiment with Bazon’s Code Sample

To show that our experiment procedure works, we tried the procedure with one of Bazon’s code samples.

| |

| |

| |

| |

|function createEl(i) { |

|var span = document.createElement("span"); |

|span.className = "muci"; |

|span.innerHTML = " foobar #"+i+" "; |

|span.onclick = function() { alert(this.innerHTML + "\n" + i); }; |

|document.body.appendChild(span); |

|}; |

| |

|function start() { |

|var T1 = (new Date()).getTime(); // DEBUG.PROFILE |

|for (var i = 0; i < 3000; ++i) |

|createEl(i); |

|alert(((new Date()).getTime() - T1) / 1000); // DEBUG.PROFILE |

|}; |

| |

| |

| |

| |

|start |

| |

| |

Figure 10 - Bazon's code sample from Error! Reference source not found., embedded into HTML.

| |

| |

| |

| |

|function createEl(i) { |

|var span = document.createElement("span"); |

|span.className = "muci"; |

|span.innerHTML = " foobar #"+i+" "; |

|span.onclick = null; |

|document.body.appendChild(span); |

|}; |

| |

|function start() { |

|var T1 = (new Date()).getTime(); // DEBUG.PROFILE |

|for (var i = 0; i < 3000; ++i) |

|createEl(i); |

|alert(((new Date()).getTime() - T1) / 1000); // DEBUG.PROFILE |

|}; |

| |

| |

| |

| |

|start |

| |

| |

Figure 11 - Control experiment, with the modified code (1 line) underlined.

Since part of the alleged reference cycle in Bazon’s code sample is the closure assigned as span’s click handler, we replaced the closure with null in our control experiment.

[pic]

Figure 12 - Drip test results of Bazon’s code sample (left) and control experiment (right).

From the screenshots above, Drip showed positive result for the code sample and negative result for the control experiment. Therefore, we arrive at the same conclusion as Bazon – the closure was part of the reference cycle, which induced memory leak. Therefore, our experiment procedure worked.

Experiments for Finding Leaky Programming Patterns under Internet Explorer 6

We designed a total of 8 experiments for determining which programming patterns would leak in Internet Explorer 6, and which would not. We do not believe that our list contains all possible programming patterns that would induce memory leaks by forming circular references, but all our cases are all very simple that we believe should provide significant insight for the construction of WT Toolkit.

The source code for the 8 experiments can be found in Appendix A of this report. The source code for the corresponding control experiments is not included because it is wasteful to print out the same code samples again with only one to two lines changed. Instead, we included instructions for producing the control experiments in the code samples that are known to leak, for readers who would like to reproduce our experimental results.

Below is a table of our experiment results:

|# |Reference |Description of Pattern |Does it Leak? |

|1 |Figure ?? |A DOM node referring to itself via an attribute. |Yes |

|2 |Figure ?? |Two DOM nodes referring to each other. |Yes |

|3 |Figure ?? |A DOM node referring to itself via a Javascript object. |Yes |

|4 |Figure ?? |Two Javascript objects referring to each other, with a DOM node |No |

| | |attached to one of the objects as an indicator such that any leak | |

| | |can be detected via Drip. | |

|5 |Figure ?? |Closure declared in the same scope as a DOM node, and assigning |Yes |

| | |that closure as the DOM node's attribute. | |

|6 |Figure ?? |Same as the last experiment, except that the DOM node is put |Yes |

| | |inside a Javascript object such that identifiers inside the | |

| | |closure cannot be bound to the DOM object directly. | |

|7 |Figure ?? |Trying to avoid circular references via closures with even more |No |

| | |closures. This test case is modified from one of the suggested | |

| | |solutions mentioned in Bazon's article. | |

|8 |Figure ?? |Making a function object by calling the Function class constructor|No |

| | |within the same scope of a DOM node, and assigning the function | |

| | |object as a DOM node attribute. | |

Two emerging patterns can be deduced from the experiment results above:

Hypothesis 1 - Javascript circular references in Internet Explorer 6 create cross-page memory leaks if and only if the reference cycle includes one or more DOM nodes in it.

Hypothesis 2 - Javascript closures in Internet Explorer 6 retain references to all outer scope variables, even though the corresponding same-name identifier is never written inside of the closure. It is therefore very easy to create circular references involving DOM nodes with closures.

[pic]

Figure 13– Relations between objects created in init() from our first four code samples, illustrated as UML instance diagram. Notice that the DOM node is not involved in the reference cycle for the fourth code sample.

Error! Reference source not found. can be justified by looking at the results from the first four code samples and comparing their source code. The first three cases all leaks memory, but the last case does not. All four cases contain circular references; the only difference is, for the last case, the DOM object is not involved in the reference cycle.

[pic]

Figure 14 – Relations between objects created in init() from code samples 5 and 6, illustrated as UML instance diagram.

Hypothesis 2 can be justified by looking at the source code of code samples number 5 and 6. Memory leak occurred in both code samples, and control experiments confirmed that the memory leak is solely resulted from the closures. Since the only known way for including the closure to leak memory is by circular reference, the closure must be part of a reference cycle. If a closure forms a part of a reference cycle, then the closure must be referring to some other objects, otherwise no cycle can be formed. In code samples 5 and 6, discounting the extra closure created for control experiment (which can be removed and there would still be memory leak), the only other object that closure “f” can refer to is “node” and “jso” respectively. The identifiers “node” or “jso” is never written inside the closure “f”. So “f” retains references from the outer scope even though the identifiers “node” or “jso” was never written inside “f”. The circular reference has nothing to do with the “this” identifier inside the closure, because the control experiment’s closure “g” in the code sample does not leak memory.

3. The Lapsed Listener Problem

[pic]

Figure 15 – An observer pattern implementation shown on Wikipedia, notice that the event publisher class Ball keeps strong references to its event subscribers of the IObserver class. In the programmer’s eyes, instances of Ball can have a very long lifecycle while instances of IObserver can have a very short lifecycle (e.g. football players may leave and join the game, yet the football is the same). This implementation is prone to the lapsed listener problem.

[33]

The lapsed listener problem is a common programming anti-pattern that causes the failure to release memory occupied by objects, even though said objects are no longer needed in the programmer’s eyes [10]. It is often the consequence of a naïvely implemented event handling system designed upon the observer design pattern. It does not go away even if the garbage collector of the underlying programming language works perfectly.

|“A lapsed listener is when an object is added to a collection but never removed. The most common example of this|

|is an event listener, where the object is added to a listener list, but never removed once it is no longer |

|needed. So the object's usefulness has lapsed because although it's still in the list, receiving events, it no |

|longer performs any useful function.” |

Figure 16 – Quote from the article “How Do You Plug Java Memory Leaks?” from the February 2000 issue of Dr. Dobbs Journal.

Not being aware of the potential danger of lapsed listeners, the signal-slot event handling system implemented in WT Toolkit prior to version 0.2.0 leaked memory whenever the programmer forgets to disconnect the slots of an unused object from the signals of other live objects, before that object is left to disuse (and supposedly left to be cleaned up by the JavaScript garbage collector). The first usable application demo written with WT Toolkit, “francium’s Xanga Tracker” [27] showed symptoms of the lapsed listener problem in its earliest versions. These included:

1. Unbounded growth of allocated memory space for all browsers (Firefox, IE6, IE7), as the user refreshes his Xanga log entries.

2. A Xanga blog page being tracked by “francium’s Xanga Tracker v0.1.3” would freeze the browser if left opened for too long (say, a few days) and is closed.

[pic]

Figure 17– Unbounded growth in allocated memory space observed with firefox.exe as the user refreshes his Xanga Log entries repeatedly in francium’s Xanga Tracker v0.1.3.

A quick check on our Xanga Tracker’s source code found a very dangerous lapsed listener pattern in the code concerning refreshing Xanga Log entries:

1. There are many short-lived DOM objects as the cells and rows in the table displaying the Xanga log entries.

2. Each one of the short-lived DOM objects is subscribed to a permanent mouse event publisher, so the mouse event publisher keeps references to all the short-lived table cells and rows.

3. Every time the Xanga log entries are refreshed, the table cells and rows disappear and are of no use to the programmer. However, the global mouse event publisher is still keeping references to them and thus they cannot be garbage collected.

It is certain that many more such lapsed listener patterns can be found by inspecting the source code further, because all events in the Xanga Tracker are handled by the signal-slot system (which is a kind of event handling mechanism based on the observer pattern) implemented in WT Toolkit. It is thus very desirable to have a general solution that can mitigate the risk of lapsed listeners in our signal-slot event handling system.

Lapsed Listeners in JavaScript

At the first glance, it seems that lapsed listeners is purely the programmer’s fault, and it should be the programmer’s job to solve lapsed listener problems in his program, instead of WT Toolkit’s job. There are even superficial parallels that one can easily draw for this argument – the “delete” keyword in C++, the free() standard library function in C - if it is acceptable for the programmer to free up the memory taken by an object or a variable in two of the most popular programming languages, why would it be unacceptable for the programmer to perform the necessary cleanup of signal-slot connections before or after an object is left to disuse in JavaScript? There’s even a “delete” keyword in JavaScript, it seems like talking about lapsed listeners in JavaScript is nonsensical.

|a = {"a":20, "b":30}; |

|b = a; |

|a.a = 80; |

|delete a; |

|alert(b.a + "," + b.b); // The string ”80,30” appears on screen |

Figure 18 – Code snippet in JavaScript. Deleting the object “a” does not erase the memory occupied by its value because the same object reference is held by the identifier “b”.

First, the semantics of the “delete” keyword in JavaScript is different from that in C++. In C++, “delete” means freeing up the memory held by a pointer unconditionally – deleting a pointer that is still being held by other objects in a C++ program would create dangling pointers, which is a potential danger. The “delete” keyword in JavaScript, however, only means unbinding an identifier from its value – if the value’s object reference is held by more than one identifier, deleting just one identifier would not free up the memory used by the value. The “delete” call in JavaScript is very much like the unlink() system function or the rm command in Unix systems in this respect – unlinking a file in Unix does not automatically mean the space held by the file is freed up, because there may be other hard links bound to the same file content.

Second, the relation between a pointer returned by malloc() or the new operator and its allocated memory area is always one-to-one in C or C++. The relationship between signals and slots in a signal-slot event handling system, however, can be many-to-many. In talking about lapsed listeners in signal-slot systems, we are mainly concerned about slots. A slot in WT Toolkit can be connected from multiple signals at the same time, and as with any decent observer design pattern implementation, the programmer should not need to know how many signals are actually connected to his slot at any moment. Therefore, requiring the programmer to disconnect each and every signal-slot connection of an object before it is left to disuse is not always possible.

What if we make the slots automatically aware of the signals that are connected to it, by, say, adding an array to the slot that remembers what signals are currently connected to it, and modifying that array in the wtObject.connect() and wtObject.disconnect() operations? This approach could be correct in other programming languages. Unfortunately, this approach is not correct in JavaScript. As shown in Section 2.2 in this report, a closure in JavaScript, under Internet Explorer at least, holds references to all locally defined variables in its outer scope even if those variables are never referenced in its inner scope. How is this relevant to lapsed listeners? Look at the following pseudo-code snippet:

|function foo() |

|{ |

|global variable a = new wtObject(); |

|global variable b = new wtObject(); |

|local variable c = a; |

|bar1 = function(){} |

|bar2 = function(){} |

|assign bar1 as a slot in a; |

|assign bar2 as a slot in b; |

|create the signal "Sig" in a; |

|create the signal "Sig" in b; |

|connect b.Sig to a.bar1; |

|connect a.Sig to b.bar2; |

|} |

|foo(); |

|disconnect all signals-slot connections connecting to and from of a; |

|delete a; |

Figure 19 – Pseudo-code snippet showing a possible solution for the lapsed listener problem in a signal-slot event handling system.

Assuming the above pseudo-code snippet was realized in JavaScript. In the end of the code snippet, although we have already disconnected any signal-slot connections connecting to and from the object “a” before leaving it to disuse, the object “b” is still holding at least one reference to the object reference that used to be “a”. Why? This is because one of the slot functions of object “b” have retained implicit references back to foo()’s local variable “c”, which holds the same object reference of the previous “a”.

Therefore, in implementing the signal-slot event handling system in WT Toolkit, we cannot rely on the programmer being careful enough to disconnect all incoming or outgoing signal-slot connections before leaving an object to disuse. Because even if he is so careful, there can be still lapsed listeners. A more aggressive solution is needed.

Lapsed Listeners in Other Web UI Toolkits

It has to be noted that the lapsed listener problem is not unique to the signal-slot event system of early WT Toolkit versions. The problem is present in the current versions of many popular web UI toolkits. In our project, it was found that Dojo Toolkit [6] and Yahoo! UI Library () are both vulnerable to lapsed listeners as of April 2007. In this aspect, it can be said that WT Toolkit’s event system is more advanced when compared to Dojo Toolkit and Yahoo! UI Library. More details about the vulnerabilities in Dojo Toolkit and Yahoo! UI Library are reported in Section 2.5 of this report.

4. Performance Implications

Despite unwanted memory consumption, memory leaks in JavaScript can have negative consequences on the latencies of operations. Some of the consequences are expected – for example, when we have lapsed listeners in an event system which is based on the observer design pattern, the time to process events would rise naturally. This is because when an event is fired, the lapsed listeners are still being notified of the event, despite the fact that they are useless. What is interesting is that we have found some unexpected consequences of JavaScript memory leaks in this project, specifically, memory leaks under Internet Explorer 6.

Object Creation Latency and Internet Explorer 6 Memory Leaks

In the article “IE: Where’s my memory?” by Mihai Bazon [2], Bazon asserted and demonstrated that the general performance of a web application with memory leak would deteriorate incrementally under Internet Explorer 6. During the development of WT Toolkit, however, it was discovered that Bazon’s statement is not generally true. During our investigation, it was discovered that not all JavaScript operations were affected by memory leak performance-wise.

For this part of our investigation, four HTML files were created, with their embedded Javascript derived from four of the leaky Javascript programs found in Section 2.2. Therefore, all four code samples used in this section shows cross-page memory leaks in Internet Explorer 6.

Since we are testing for performance in this section, it is important to mention the machines on which the experiments were performed. We executed each of the four code samples on the two machines below:

Machine 1 – A high end desktop

Intel Core 2 Duo E6400 overclocked to 3GHz

1GB memory

Windows XP SP2 with IE6

Machine 2 – Any random machine in FYP lab

Intel Pentium 4 3.2GHz

1GB memory

Windows XP SP2 with IE6

The four code samples:

|# |Reference |Description |

|1 |Figure ?? |Each time the link “Create 2000 DIV nodes” is clicked, 2000 self-referencing |

| | |DOM nodes are created. |

|2 |Figure ?? |Each time the link “Create 2000 DIV nodes” is clicked, 1000 pairs of circular|

| | |referencing DIV nodes are created. |

|3 |Figure ?? |Each time the link “Create 2000 DIV nodes” is clicked, 2000 DIV nodes each |

| | |with a reference to itself via a Javascript object is created. |

|4 |Figure ?? |Each time the “Create 2000 DIV nodes” link is clicked, 2000 DIV nodes each |

| | |with a reference to itself via a closure is created. |

The operation flow of the four code samples is very similar to Bazon’s code samples for demonstrating memory leak and deteriorating performance in Internet Explorer 6:

1. A single link, “Create 2000 DIV nodes” appears when the HTML file is loaded.

2. Each time the link is clicked

i. The system time is recorded as the start time.

ii. 2000 DIV nodes are added to the page by Javascript

iii. The system time is recorded as the end time.

iv. The operation latency is calculated by (end time – start time) and displayed.

The initial experiment procedure is as follows:

1. Load one of the HTML files to Internet Explorer 6;

2. Click on the “Create 2000 DIV nodes” link;

3. Record the committed address space size of iexplorer.exe by reading the VM size column from Task Manager;

4. Record the operation latency displayed by the code sample;

5. Refresh the page;

6. Repeat steps 2 to 5 by 10 times;

7. Repeat steps 1 to 6 until all (machine, code sample) combinations have been tested.

No control experiment was designed initially because it was not known which JavaScript operation would be affected by the memory leak. Control experiments were performed after hypotheses were drawn on the affected operations.

Here are the initial experiment results:

[pic]

Figure 20- Allocated Memory vs. Trial on High End Desktop.

[pic]

Figure 21 - Operation Latency vs. Trial on High End Desktop.

[pic]

Figure 22 - Allocated Memory vs. Trial on FYP Lab PC.

[pic]

Figure 23 - Operation Latency vs. Trial on FYP Lab PC.

Although the memory consumption graphs showed that all four test cases leaks memory incrementally between trials, the operation latency of code samples 1 and 2 stayed constant. This observation comes in contrary to what Bazon claimed.

Comparing the source code of code samples 1, 2 with code samples 3, 4, one of the significant differences between them is that JavaScript objects were created and subsequently leaked away in circular references with DOM nodes in code samples 3, 4, while no JavaScript objects were created with each DOM node in code samples 1, 2. This might seem ridiculous in the first glance, isn’t a DOM node also a JavaScript object? They are not.

So we draw the following hypothesis:

Hypothesis 3 – JavaScript object creation latency deteriorates with the number of JavaScript objects leaked in circular references involving DOM nodes, under Internet Explorer 6.

To verify our hypothesis, we can attach JavaScript objects to the leaked DOM nodes in code samples 1 and 2, and see if the operation latencies would increase or not.

So we performed control experiments with code samples 1 and 2 with a modified placeNode() below:

|function placeNode(node) |

|{ |

|// place the node randomly on the page |

|var x = parseInt(Math.random() * 800) + "px"; |

|var y = parseInt(50 + Math.random() * 750) + "px"; |

|node.someAttr = new Object; |

|node.style.position = "absolute"; |

|node. = y; |

|node.style.left = x; |

|node.style.width = "1px"; |

|node.style.height = "1px"; |

|node.style.fontSize = "1px"; |

|node.style.backgroundColor = "black"; |

|} |

Figure 24 - Modified placeNode() function that is predicted to show deteriorating performance with memory leak. Modified code is underlined.

The control experiments were performed on the high-end desktop machine. The results are as follows:

[pic]

Figure 25 – Operation Latency vs. Trial on High End Desktop, control experiment.

A slight, but obvious increasing trend in operation latency was observed in the control experiment. This confirms our hypothesis that the creation and subsequent leak of JavaScript objects is the real reason for the deteriorating performance.

General JavaScript Object Creation Latencies in Internet Explorer 6

The experiments above showed that when JavaScript objects are being leaked away with DOM circular references, subsequent JavaScript object creation latencies would increase. However, during the development of our project, it was noticed that deteriorating object creation latencies can still happen under Internet Explorer 6 even if there are no memory leaks.

We found that in Internet Explorer 6, JavaScript object creation latencies would increase with the number of reachable JavaScript objects in the web browser, in addition to the number of JavaScript objects leaked away with DOM circular references. The same kind of performance deterioration is not present in Firefox or Internet Explorer 7.

Hypothesis 4 – JavaScript object creation latency deteriorates with the number of JavaScript objects that are still in memory, under Internet Explorer 6.

Two experiments were designed to justify our hypothesis:

|# |Reference |Description |

|1 |Figure ?? |Demonstration code for showing JavaScript object creation latency |

| | |deteriorating with number of reachable JavaScript Objects, under Internet |

| | |Explorer 6 and 7. |

|2 |Figure ?? |Control experiment. Here, although the memory usage of iexplorer.exe is also |

| | |increasing progressively, JavaScript object creation latency does not |

| | |deteriorate because the number of reachable JavaScript objects is not |

| | |increasing. |

The experiments above were designed to be fully automated. A “Run Benchmark” link appears in the browser window once the above code samples are loaded.

[pic]

Figure 26 – Screenshot of code sample 1 running.

The program flow of code sample 1 after clicking “Run Benchmark” is as follows:

1. Go to step 3.

2. Create 20000 JavaScript objects and store them to a global array.

3. Record the current system time as startTime.

4. Create 10000 JavaScript objects and throw them away.

5. Record the current system time as endTime.

6. Print out the time taken to create the 10000 JavaScript objects by calculating endTime – startTime.

7. Repeat steps 2 to 6 until there are 400000 or more objects in the global array.

[pic]

Figure 27 - Screenshot of code sample 2 running.

Code sample 2 is a control experiment which creates DOM nodes instead of JavaScript objects for filling up the browser’s memory space:

1. Go to step 3.

2. Create 2000 DOM nodes and add them under document.body.

3. Record the current system time as startTime.

4. Create 10000 JavaScript objects and throw them away.

5. Record the current system time as endTime.

6. Print out the time taken to create the 10000 JavaScript objects by calculating endTime – startTime.

7. Repeat steps 2 to 6 until there are 40000 or more DOM nodes under document.body.

Notice that the number of “filler” DOM nodes in code sample 2 is only one tenth the number of “filler” JavaScript objects in code sample 1. This is because DOM nodes take considerably more memory than empty JavaScript objects.

[pic]

Figure 28 – Experiment with code sample 1 shows increasing object creation latency with the number of reachable JavaScript objects.

[pic]

Figure 29 - Control experiment (code sample 2) with DOM nodes instead of JavaScript objects that are stored in the memory.

The experimental results above showed that, under Internet Explorer 6, JavaScript object creation latencies increases with the number of reachable JavaScript objects, but not other factors that would also increase memory usage (like number of DOM nodes in the current web page).

5. The Search for Solutions

We tried to find solutions to the memory leak problems we discovered by first looking at how other popular client side Web UI toolkits approached them, and failing that, we looked for solutions in other programming languages.

Case Study 1: Dojo Toolkit

We studied Dojo Toolkit first, because from reading one of the Alex Russell’s (Project founder of Dojo Toolkit) blog entries [23], it seems that he was very well aware of the memory leak problems and he had already implemented a reasonable solution.

Actually writing test cases for Dojo Toolkit, unfortunately, turned up with the same symptoms of lapsed listeners as WT Toolkit does. Two code samples were written for experimentation:

| |

| |

| |

|Dojo Button Widget Test |

| |

| |

|dojo.require("dojo.widget.Button"); |

| |

| |

| |

| |

| |

|var widgetArray = []; |

| |

|function createOne() |

|{ |

|var widget = dojo.widget.createWidget("Button", {"caption":"Hi!"}); |

|var node = widget.domNode; |

|node.style.position = "absolute"; |

|node. = parseInt(Math.random() * 800) + "px"; |

|node.style.left = parseInt(Math.random() * 800) + "px"; |

|dojo.body().appendChild(node); |

|widgetArray.push(widget); |

|} |

| |

|function destroyAll() |

|{ |

|while(widgetArray.length > 0) |

|{ |

|var widget = widgetArray[0]; |

|var node = widget.domNode; |

|widgetArray.splice(0,1); |

|node.parentNode.removeChild(node); |

|dojo.event.browser.clean(node); |

|} |

|} |

| |

|function createMany() |

|{ |

|for(var i=0;i 0) |

|{ |

|var node = widgetArray[0]; |

|widgetArray.splice(0,1); |

|node.parentNode.removeChild(node); |

|dojo.event.browser.clean(node); |

|} |

|} |

| |

|function createMany() |

|{ |

|for(var i=0;i DOM node |

|// for control experiment, assign null instead. |

|node.circularReference = node; |

| |

|// finally, we attach the DOM node to the document tree |

|// this step is not required for the leak to happen |

|document.body.appendChild(node); |

|} |

| |

| |

| |

| |

| |

Figure 82 - A DOM node referring to itself via an attribute.

| |

| |

|Circular reference between a DOM object and another DOM object leaks memory in Internet |

|Explorer |

| |

| |

|// this is actually worse than the other cases - both nodes are leaked because both are referenced in a |

|// circle |

|function init() |

|{ |

|// construct the first DOM node and attach it to the document tree |

|var node = document.createElement("div"); |

|node.innerHTML = "I'm leaked away!"; |

|node.id = "node"; |

|document.body.appendChild(node); |

| |

|// construct the second DOM node and attach it to the document tree |

|var intermediateNode = document.createElement("div"); |

|intermediateNode.id = "intermediateNode"; |

|intermediateNode.innerHTML = "I'm also leaked away!"; |

|document.body.appendChild(intermediateNode); |

| |

|// now, make a circular reference path like this: |

|// DOM node A -> DOM node B -> DOM node A |

|// for control experiment, comment out any one of the following two lines |

|node.circularReferencePart1 = intermediateNode; |

|intermediateNode.circularReferencePart2 = node; |

|} |

| |

| |

| |

| |

| |

Figure 83 - Two DOM nodes referring to each other.

| |

| |

|Circular reference between a JS object and a DOM object leaks memory in Internet Explorer |

| |

|function init() |

|{ |

|// first, we construct a DOM node |

|var node = document.createElement("div"); |

|node.innerHTML = "I'm leaked away!"; |

| |

|// then, we make a Javascript object |

|var jsobj = new Object(); |

| |

|// then, we make a circular reference like this: |

|// DOM node -> JS object -> DOM node |

|// for control experiment, comment out any one of the two lines below |

|node.circularReferencePart1 = jsobj; |

|jsobj.circularReferencePart2 = node; |

| |

|// finally, we attach the DOM node to the document tree |

|// this step is not required for the leak to happen |

|document.body.appendChild(node); |

|} |

| |

| |

| |

| |

| |

Figure 84 - A DOM node referring to itself via a Javascript object.

| |

| |

|Circular reference between two Javascript objects does not leak memory in Internet Explorer |

| |

| |

|function init() |

|{ |

|// first, we construct a DOM node and attach it the the document tree |

|var node = document.createElement("div"); |

|node.innerHTML = "I'm NOT leaked away!"; |

|node.id = "node"; |

|document.body.appendChild(node); |

| |

|// then, we construct a Javascript object and add the DOM node to it. |

|var jsobj = new Object(); |

|jsobj.domNode = node; |

| |

|// then, we construct another Javascript object |

|var jsobj2 = new Object(); |

| |

|// finally, we create a circular reference like this: |

|// JS object A -> JS object B -> JS object A |

|jsobj.circularReference1 = jsobj2; |

|jsobj2.circularReference2 = jsobj; |

|} |

| |

| |

| |

| |

| |

Figure 85 - Two Javascript objects referring to each other, with a DOM node attached to one of the objects as an indicator such that any leak can be detected via Drip.

2. Leak Experiments with Closures

| |

| |

|Circular reference between DOM node and a closure via implicit binding of variables leaks memory in |

|Internet Explorer |

| |

|function makeAnotherClosureForControlExperiment() |

|{ |

|window.g = function(){alert(this);} |

|} |

| |

|function init() |

|{ |

|// first, we construct a DOM node |

|var node = document.createElement("div"); |

|node.innerHTML = "I'm leaked away!"; |

| |

|// then, we create an empty closure |

|// it doesn't even need to refer to "node" or "this" |

|// for the circular reference to happen |

|var f = function(){} |

| |

|// then, we create a circular reference like this: |

|// DOM node -> closure -> DOM node |

|// if you want to perform a control experiment, comment out the line below |

|node.f = f; |

| |

|// for a control experiment, we make another closure |

|// that cannot possibly refer back to "node" implicitly |

|// but rather, it can refer back to "node" via the "this" reference |

|// interestingly, this one does NOT leak |

|makeAnotherClosureForControlExperiment(); |

|node.g = window.g; |

| |

|// finally, we attach the DOM node to the document tree |

|// this step is not required for the leak to happen |

|document.body.appendChild(node); |

|} |

| |

| |

| |

| |

| |

Figure 86 - Closure declared in the same scope as a DOM node, and assigning that closure as the DOM node's attribute.

| |

| |

|This example shows that containing a DOM node within a Javascript object cannot prevent the memory |

|leak |

| |

|function makeAnotherClosureForControlExperiment() |

|{ |

|window.g = function(){alert(this);} |

|} |

| |

|function init() |

|{ |

|// first, we construct a DOM node, contained in a JS object |

|var jso = {"node" : document.createElement("div")}; |

|jso.node.innerHTML = "I'm leaked away!"; |

| |

|// then, we create an empty closure |

|// it doesn't even need to refer to "node" or "this" |

|// for the circular reference to happen |

|// if you want to perform a control experiment, comment out the line below |

|var f = function(){} |

| |

|// then, we create a circular reference like this: |

|// DOM node -> closure -> JS Object -> DOM node |

|jso.node.f = f; |

| |

|// for a control experiment, we make another closure |

|// that cannot possibly refer back to "node" implicitly |

|// but rather, it can refer back to "node" via the "this" reference |

|// interestingly, this one does NOT leak |

|makeAnotherClosureForControlExperiment(); |

|jso.node.g = window.g; |

| |

|// finally, we attach the DOM node to the document tree |

|// this step is not required for the leak to happen |

|document.body.appendChild(jso.node); |

|} |

| |

| |

| |

| |

| |

Figure 87 – Same as the last experiment, except that the DOM node is put inside a Javascript object such that identifiers inside the closure cannot be bound to the DOM object directly.

| |

| |

| |

|This examples shows that creating the DOM node with another closure prevents the memory leak |

| |

|function init() |

|{ |

|// first, create an empty closure |

|var f = function(){} |

|var h = function() |

|{ |

|// now, we construct a DOM node |

|var node = document.createElement("div"); |

|node.innerHTML = "I'm NOT leaked away!"; |

| |

|// notice that no circular reference has been created |

|// because the closure f cannot refer back to our DOM node via "node" |

|// but it can still do so via "this" |

|node.f = f; |

| |

|// finally, we attach the DOM node to the document tree |

|// this step is not required for the leak to happen |

|document.body.appendChild(node); |

|} |

|h(); |

|} |

| |

| |

| |

| |

| |

Figure 88 - Trying to avoid circular references via closures with even more closures. This test case is modified from one of the suggested solutions mentioned in Bazon's article.

3. Leak Experiment with Function Objects

| |

| |

|Circular reference between DOM node and a non-closure function object does not leaks memory in Internet|

|Explorer |

| |

|function init() |

|{ |

|// first, we construct a DOM node |

|var node = document.createElement("div"); |

|node.innerHTML = "I'm NOT leaked away!"; |

| |

|// then, we create an empty function object |

|// a function object created this way cannot refer to variables defined in the outer scope |

|var f = new Function(""); |

| |

|// then, we create a circular reference like this: |

|// DOM node -> function object -> DOM node |

|node.f = f; |

| |

|// finally, we attach the DOM node to the document tree |

|document.body.appendChild(node); |

|} |

| |

| |

| |

| |

| |

Figure 89 - Making a function object by calling the Function class constructor within the same scope of a DOM node, and assigning the function object as a DOM node attribute.

10. Appendix B: Source Code for Finding JavaScript Operations Affected by Memory Leaks in Internet Explorer 6

| |

| |

|Circular reference within a DOM object leaks memory in Internet Explorer |

| |

|function leakOne() |

|{ |

|// first, we construct a DOM node |

|var node = document.createElement("div"); |

| |

|// then, we make a circular reference like this: |

|// DOM node -> DOM node |

|node.circularReference = node; |

| |

|// finally, we attach the DOM node to the document tree |

|// this step is not required for the leak to happen |

|document.body.appendChild(node); |

| |

|placeNode(node); |

|} |

| |

|function placeNode(node) |

|{ |

|// place the node randomly on the page |

|var x = parseInt(Math.random() * 800) + "px"; |

|var y = parseInt(50 + Math.random() * 750) + "px"; |

|node.style.position = "absolute"; |

|node. = y; |

|node.style.left = x; |

|node.style.width = "1px"; |

|node.style.height = "1px"; |

|node.style.fontSize = "1px"; |

|node.style.backgroundColor = "black"; |

|} |

| |

|function getTime() |

|{ |

|return (new Date()).getTime(); |

|} |

| |

|function leak2000() |

|{ |

|var begin = getTime(); |

|for(var i=0;i DOM node B -> DOM node A |

|node.circularReferencePart1 = intermediateNode; |

|intermediateNode.circularReferencePart2 = node; |

| |

|placeNode(node); |

|placeNode(intermediateNode); |

|} |

| |

|function placeNode(node) |

|{ |

|// place the node randomly on the page |

|var x = parseInt(Math.random() * 800) + "px"; |

|var y = parseInt(50 + Math.random() * 750) + "px"; |

|node.style.position = "absolute"; |

|node. = y; |

|node.style.left = x; |

|node.style.width = "1px"; |

|node.style.height = "1px"; |

|node.style.fontSize = "1px"; |

|node.style.backgroundColor = "black"; |

|} |

| |

|function getTime() |

|{ |

|return (new Date()).getTime(); |

|} |

| |

|function leak2000() |

|{ |

|var begin = getTime(); |

|for(var i=0;i JS object -> DOM node |

|node.circularReferencePart1 = jsobj; |

|jsobj.circularReferencePart2 = node; |

| |

|// finally, we attach the DOM node to the document tree |

|// this step is not required for the leak to happen |

|document.body.appendChild(node); |

| |

|placeNode(node); |

|} |

|function placeNode(node) |

|{ |

|// place the node randomly on the page |

|var x = parseInt(Math.random() * 800) + "px"; |

|var y = parseInt(50 + Math.random() * 750) + "px"; |

|node.style.position = "absolute"; |

|node. = y; |

|node.style.left = x; |

|node.style.width = "1px"; |

|node.style.height = "1px"; |

|node.style.fontSize = "1px"; |

|node.style.backgroundColor = "black"; |

|} |

| |

|function getTime() |

|{ |

|return (new Date()).getTime(); |

|} |

| |

|function leak2000() |

|{ |

|var begin = getTime(); |

|for(var i=0;i Hi, |

|> |

|> I've sent this question three times to the mailing list yesterday and for |

|> the first two times the mailing server thought I was spamming it. The |

|> mailing server didn't respond to my third mail, but by now I suspect it |

|> was silently marked as spam and dropped. So I'm sending this question |

|> again from a different email address. |

|> |

|> Here's my previous mail, if it is repeated, sorry about that: |

|> |

|> I'm new to Dojo Toolkit - I've just started learning it last week. I |

|> started learning it by writing some simple, proof-of-concept dynamic pages |

|> (where widgets can be created and destoryed without being refreshed - I |

|> haven't touched on the ajax stuff yet). I think I've done something wrong, |

|> and I've tried things like dojo.event.browser.clean() that popped up in |

|> this mailing list and Alex's December 2005 blog entries for similar |

|> problems, but to no avail. |

|> |

|> First page I constructed with Dojo: |

|> |

|> |

|> Second page I constructed with Dojo: |

|> |

|> |

|> In both examples, clicking "Creating xxx widgets" and then "Destroy All" |

|> repeatedly in IE6 or IE7 would lead to the vmsizes of both browsers (as |

|> observed from Window's Task Manager) increase rapidly. How can I correct |

|> this problem? And could anyone kindly point to the reason why is it |

|> happening? |

|> |

|> Best regards, |

|> Martin Kou |

|> |

|> _______________________________________________ |

|> Dojo FAQ: |

|> Dojo Book: |

|> Dojo-interest at |

|> |

Figure 96 – Original problem statement.

URL:

|Thanks for the information. It does indeed like what you say. I tested on |

|two browsers(IE 7.0 and Opear9.01). |

|Well the destroy is only cleaning up the browser but not the memory. In fact |

|it adds to the memeory size after cleaning the browser!. |

| |

|In my Task manager I cannot find the Memory Usage History tab which would |

|have given more information. References on the web gives a hint as to why |

|this may be happening. The leakage problem is often associated with the dom |

|elements (the widget) being created inside a script. |

|The following IE specific article may give you a hint: |

| |

| |

|Jayaram |

Figure 97 – Third party confirmation of problem.

URL:

|In all fairness to dojo the increase is not exponential for Martin's example code. |

| |

|At first 19240 K |

|First click 25108 K |

|Second Click 29960 K |

|Third click 35544 K |

|Fourth click 40540 K |

|Fifth click 46104 k |

|At Destroy 583428K |

| |

|But the effect is substantial slowdown. |

Figure 98 – Third party confirmation of problem.

URL:

|Hi Martin, |

| |

|On Wednesday 03 January 2007 10:22 am, Martin Kou wrote: |

|> I've read that MSDN article quite a long while ago (I think January |

|> 2006? I so wished Microsoft would crash and burn upon reading that |

|> article for the first time), it mostly deals with memory leaks |

|> related to circular references in which IE's garbage collector cannot |

|> clean up. As far as I know, I believe such memory leaks can always be |

|> detected with Drip if the circular references still exist after the |

|> page is unloaded. |

| |

|Drip tries to "force" the issue in a wrapped IE ActiveX control, but |

|I've observed leakage that Drip can't find (in the XHR object, for |

|instance). |

| |

|> I think the leak I'm seeing here is different. Testing the pages with |

|> Drip turns up with negative results. Also, as I understand, Dojo's |

|> event system does not store the event handler given directly under |

|> the subject - it stores the handler elsewhere and puts a generic |

|> handler function to, say, the onclick attribute. So I believe it is |

|> not possible for the "traditional" DOM object -> closure -> DOM |

|> object kind of leak to happen under Dojo, as long as I use its event |

|> system instead of assigning closures to DOM nodes directly. |

|> |

|> In the createOne() function of the dojo_leak_dom.html test case, |

|> there exists a closure -> DOM object link between "node" and |

|> "clickHandler", because clickHandler can refer back to node |

|> implicitly. Just take my word that such a reference exists even if |

|> the clickHandler is an empty closure, but I can provide a proof to |

|> you if you care. |

| |

|No, I believe you. There's enough else wrong w/ the JScript heuristics |

|around memory that it wouldn't surprise me that the closure dehydration |

|code is over-broad. That, or it's a hedge against runtime deref w/ |

|something like obj["propName"], but even that doesn't make much sense. |

| |

|> Now, assume Dojo's event system does not create circular references |

|> with dojo.event.connect() - this assumption is reasonable because |

|> Dojo's event system was claimed to be designed specifically to avoid |

|> this problem (). It can, however, |

|> still leak memory before a page is unloaded by doing one of the two |

|> things: |

| |

|It should be noted that the reference is *created*, we just take pains |

|to clean out the dom -> js references before final page GC happens. |

| |

|> 1. Keeping a reference to the DOM node, even after it is detached |

|> from the document tree |

| |

|Shouldn't be a problem. It's the reference in the other direction that |

|the collector can't find (and therefore can't free). That's the |

|reference set we try hard to break. |

| |

|> 2. Keeping a reference to the clickHandler, even after its associated |

|> DOM node is detached from the document tree |

| |

|See last note. |

| |

|> These items "leak" memory because as long as I keep a reference that |

|> can be traced back to the DOM node, the DOM node would not be freed, |

|> even by a correctly implemented garbage collector (So Firefox leaks |

|> memory in my dojo_leak_dom.html test cases as well... it leaks slower |

|> than IE, but the leak is still observable). |

|> |

|> Item 2 can leak memory because the clickHandler keeps an implicit |

|> reference back to the DOM node. But right now I'm guessing point 1 is |

|> true now because, following Matthew's advice, I've tried to set node |

|> = null at the end of the createOne() function, thus breaking the |

|> clickHandler -> node link |

| |

|See above note. The DOM object is a COM-bound C++ representation of the |

|DOM item, and it's the reference held *by that C++ structure* that the |

|JScript interpreter's GC chokes on. References in JS to the object |

|should be OK since the refcount can be decremented correctly when the |

|environment is cleared out. |

| |

|Obviously, this is predicated on pseudo-sane behavior from IE, an |

|assumption that has failed me in the past, but my experimental evidence |

|tends to suggest that removing the dom -> js linkage is enough to |

|prevent leakage. |

| |

|You might add a call to JScript-specific window.CollectGarbage() method |

|to ensure that your test page is really showing you what you think it's |

|showing you. |

| |

|Regards |

|[snipped] |

|-- |

|Alex Russell |

|alex at A99F 8785 F491 D5FD 04D7 ACD9 4158 FFDF 2894 6876 |

|alex at BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723 |

Figure 99 – First response from Alex Russell, Dojo project founder.



|On Wednesday 03 January 2007 1:12 am, Martin Kou wrote: |

|> Here's a video of what happend, notice the VM size was increasing |

|> rapidly as I clicked create and destroy. This is incorrect behavior |

|> since the VM size indicates the virtual memory address space |

|> allocated to the browser. |

|> |

|> |

|> |

|> (2.1MB video warning) |

| |

|Just to be very clear, unless I'm seeing the video wrong, what you're |

|describing is increasing memory usage *in a single page*. Leakage, as |

|is usually defined WRT MSIE, is defined by un-recovered memory across |

|page loads, not an increase in memory footprint inside the same page |

|environment. |

| |

|We may be able to do something about the later, but we've spend our time |

|dealing with the former to date, as it has truly debilitating |

|consequences for long-term usage. |

| |

|Regards |

|[snipped] |

|-- |

|Alex Russell |

|alex at A99F 8785 F491 D5FD 04D7 ACD9 4158 FFDF 2894 6876 |

|alex at BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723 |

Figure 100 – Second response from Alex Russell, showing that Dojo is still focused on solving memory leakage problems caused by circular referencing in Internet Explorer 6.

URL:

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download

To fulfill the demand for quickly locating and searching documents.

It is intelligent file search solution for home and business.

Literature Lottery