Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 68 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
68
Dung lượng
1,37 MB
Nội dung
Designing for performance 311 this.front.backingObj=this; } MyObject is a user-defined type. Every instance will refer to a DOM node as this.front, and the DOM node will refer back to the JavaScript object as this.backingObj. To remove this circular reference while finalizing the object, we might offer a method such as this: MyObject.prototype.finalize=function(){ this.front.backingObj=null; this.front=null; } By setting both references to null, we break the circular reference. Alternatively, a DOM tree could be cleaned up in a generic fashion, by walking the DOM tree and eliminating references on the basis of name, type, or whatever. Richard Cornford has suggested such a function, specifically for dealing with event handler functions attached to DOM elements (see the Resources section at the end of this chapter). My feeling is that generic approaches such as this should be used only as a last resort, as they may scale poorly to the large document trees typified by Ajax rich clients. A structured pattern-based approach to the codebase should enable the programmer to keep track of the specific cases where cleanup is required. A second point worth noting for IE is that a top-level “undocumented” func- tion called CollectGarbage() is available. Under IE v6, this function exists and can be called but seems to be an empty stub. We have never seen it make a differ- ence to reported memory in the Task Manager. Now that we understand the issues of memory management, let’s explore the practicalities of measuring it and applying those measurements to a real- life application. 8.4 Designing for performance We stated at the outset that performance consisted of both good execution speed and a controllable memory footprint. We also said that design patterns could help us to achieve these goals. In this section, we’ll see how to measure memory footprint in real applica- tions, and we’ll use a simple example to show how the use of design patterns can help us to understand the fluctuations in memory footprint that we may see in working code. Licensed to jonathan zheng <yiyisjun@gmail.com> 312 CHAPTER 8 Performance 8.4.1 Measuring memory footprint When we measured execution speed, we could do so either in JavaScript code using the Date object or with an external tool. JavaScript doesn’t provide any built-in capabilities to read system memory usage, so we’re dependent on exter- nal tools. Fortunately, we have several to choose from. There are a variety of ways to see how much memory your browser is consum- ing during execution of your application. The simplest way to do so is to use a sys- tem utility appropriate to your operating system to see the underlying processes. On Windows systems, there is the Task Manager, and UNIX systems have the con- sole-based top command. Let’s look at each of these in turn. Windows Task Manager The Windows Task Manager (figure 8.5) is available on many versions of Windows (Windows 95 and 98 users are out of luck here). It provides a view of all processes running in the operating system and their resource use. It can usually be invoked Figure 8.5 Windows Task Manager showing running processes and their memory usage. Processes are being sorted by memory usage, in descending order. Licensed to jonathan zheng <yiyisjun@gmail.com> Designing for performance 313 from the menu presented to the user when she presses the Ctrl+Alt+Delete key combination. The Task Manager interface has several tabs. We are interested in the tab labeled Processes. The highlighted row shows that Firefox is currently using around 38MB of memory on our machine. In its default state, the Mem Usage column provides information on active memory usage by the application. On some versions of Windows, the user can add extra columns using the View > Select Columns menu (figure 8.6). Showing the Virtual Memory Size of a process as well as Memory Usage can be useful. Memory Usage represents active memory assigned to an application, whereas Virtual Memory Size represents inactive memory that has been written to the swap partition or file. When a Windows application is minimized, the Mem Usage will typically drop considerably, but VM Size will stay more or less flat, Figure 8.6 Selecting additional columns to view in the Task Manager’s Processes tab. Virtual Memory Size shows the total amount of memory allocated to the process. Licensed to jonathan zheng <yiyisjun@gmail.com> 314 CHAPTER 8 Performance indicating that the application still has an option to consume real system resources in the future. UNIX top A console-based application for UNIX systems (including Mac OS X), top shows a very similar view of processes to the Windows Task Manager (figure 8.7). As with Task Manager, each line represents an active process, with columns showing memory and CPU usage and other statistics. The top application is driven by keyboard commands, which are documented in the man or info pages and on the Internet. Space precludes a fuller tutorial on top here, or an explora- tion of the GUI equivalents such as the GNOME System Manager that may be present on some UNIX/Linux systems. Power tools Beyond these basic tools, various “power tools” are available for tracking memory usage, offering finer-grained views of the operating system’s internal state. We can’t do justice to the full range of these tools, but here are brief pointers to a cou- ple of freeware tools that we have found useful. Figure 8.7 UNIX top command running inside a console, showing memory and CPU usage by process. Licensed to jonathan zheng <yiyisjun@gmail.com> Designing for performance 315 First, Sysinternal.com’s Process Explorer tool (figure 8.8) is perhaps best described as a “task manager on steroids.” It fulfills the same role as Task Man- ager but allows for detailed drilldown into the memory footprint and proces- sor use of individual processes, allowing us to target Internet Explorer or Firefox specifically. Second, J. G. Webber has developed Drip (see the Resources section), a simple but powerful memory management reporter for Internet Explorer that directly queries an embedded web browser about its known DOM nodes, including those that are no longer attached to the document tree (figure 8.9). However, even with the basic tools, we can discover a lot about the state of a running Ajax application. Figure 8.8 Process Explorer provides detailed reporting on memory and processor usage on a per-process basis, allowing for more accurate tracking of the browser’s footprint on a Windows machine. This window is tracking an instance of Mozilla Firefox running the stress test described in section 8.4.2. Licensed to jonathan zheng <yiyisjun@gmail.com> 316 CHAPTER 8 Performance So far, we’ve looked at individual patterns and idioms for handling perfor- mance issues in small sections of code. When we write an Ajax application of even moderate size, the various patterns and idioms in each subsystem can interact with each other in surprising ways. The following section describes a case study that illustrates the importance of understanding how patterns com- bine with one another. 8.4.2 A simple example In our discussion thus far, we have covered the theory of memory management and described a few patterns that might help us when programmatically creating interface elements. In a real-world Ajax application, we will employ several pat- terns, which will interact with one another. Individual patterns have impacts on performance, but so do the interactions between patterns. It is here that having access to a common vocabulary to describe what your code is doing becomes very valuable. The best way to illustrate this principle is by example, so in this section we introduce a simple one and present the performance impact of varying the combination of patterns that it uses. In the simple test program, we can repeatedly create and destroy small Click- Box widgets, so called because they are little boxes that the user can click on with Figure 8.9 The Drip tool allows detailed queries on the internal state of Internet Explorer’s DOM tree. Licensed to jonathan zheng <yiyisjun@gmail.com> Designing for performance 317 the mouse. The widgets themselves have a limited behavior, described by the fol- lowing code: function ClickBox(container){ this.x=5+Math.floor(Math.random()*370); this.y=5+Math.floor(Math.random()*370); this.id="box"+container.boxes.length; this.state=0; this.render(); container.add(this); } ClickBox.prototype.render=function(){ this.body=null; if (this.body==null){ this.body=document.createElement("div"); this.body.id=this.id; } this.body.className='box1'; this.body.style.left=this.x+"px"; this.body.style.top=this.y+"px"; this.body.onclick=function(){ var clickbox=this.backingObj; clickbox.incrementState(); } } ClickBox.prototype.incrementState=function(){ if (this.state==0){ this.body.className='box2'; }else if (this.state==1){ this.hide(); } this.state++; } ClickBox.prototype.hide=function(){ var bod=this.body; bod.className='box3'; } When first rendered, the ClickBoxes are red in appearance. Click on them once, and they turn blue. A second click removes them from view. This behavior is implemented by creating two-way references between the domain model object and the DOM element that represents it onscreen, as discussed earlier. Programmatically, each ClickBox consists of a unique ID, a position, a record of its internal state (that is, how many clicks it has received), and a body. The body Licensed to jonathan zheng <yiyisjun@gmail.com> 318 CHAPTER 8 Performance is a DOM node of type DIV. The DOM node retains a reference to the backing object in a variable called backingObj. A Container class is also defined that houses ClickBox objects and maintains an array of them, as well as a unique ID of its own: function Container(id){ this.id=id; this.body=document.getElementById(id); this.boxes=new Array(); } Container.prototype.add=function(box){ this.boxes[this.boxes.length]=box; this.body.appendChild(box.body); } Container.prototype.clear=function(){ for(var i=0;i<this.boxes.length;i++){ this.boxes[i].hide(); } this.boxes=new Array(); report("clear"); newDOMs=0; reusedDOMs=0; } A screenshot of the application is shown in figure 8.10. Figure 8.10 Our memory management demo application, after creation of the first 100 widgets. T he user has just clicked one of the widgets with the mouse. Licensed to jonathan zheng <yiyisjun@gmail.com> Designing for performance 319 The debug panel on the right reports on the internal state of the system after var- ious user events, such as adding or removing widgets from the container. The code has been written to allow us to swap in different patterns for creation and destruction of DOM elements and cyclic references while the application is running. The user may choose between these at runtime by checking and unchecking HTML form elements on the page. When the links that add or remove boxes from the container are activated, the combination of patterns that is used to implement the user interface will match the state of the checkboxes. Let’s examine each of these options and the corresponding code. Reuse DOM Nodes checkbox Checking this option will determine whether the ClickBox widget will try to find an existing DOM node when creating itself and create a new one only as a last resort. This allows the application to switch between the Create Always and Cre- ate If Not Exists patterns that we discussed in section 8.3.2. The modified render- ing code follows: ClickBox.prototype.render=function(){ this.body=null; if (reuseDOM){ this.body=document.getElementById(this.id); } if (this.body==null){ this.body=document.createElement("div"); this.body.id=this.id; newDOMs++; }else{ reusedDOMs++; } this.body.backingObj=this; this.body.className='box1'; this.body.style.left=this.x+"px"; this.body.style.top=this.y+"px"; this.body.onclick=function(){ var clickbox=this.backingObj; clickbox.incrementState(); } } Unlink On Hide checkbox When a ClickBox is removed from the container (either by a second click or by calling Container.clear() ), this switch will determine whether it uses the Remove By Hiding or Remove By Detachment pattern (see section 8.3.2): ClickBox.prototype.hide=function(){ var bod=this.body; Licensed to jonathan zheng <yiyisjun@gmail.com> 320 CHAPTER 8 Performance bod.className='box3'; if (unlinkOnHide){ bod.parentNode.removeChild(bod); } } Break Cyclic References checkbox When removing a ClickBox widget, this toggle determines whether the refer- ences between the DOM element and the backing object are reset to null or not, using the Break Cyclic References pattern in an attempt to appease the Internet Explorer garbage collector: ClickBox.prototype.hide=function(){ var bod=this.body; bod.className='box3'; if (unlinkOnHide){ bod.parentNode.removeChild(bod); } if (breakCyclics){ bod.backingObj=null; this.body=null; } } Form controls allow the user to add ClickBoxes to the container and to clear the container. The application may be driven manually, but for the purposes of gath- ering results here, we have also written a stress-testing function that simulates sev- eral manual actions. This function runs an automatic sequence of actions, in which the following sequence is repeated 240 times: 1 Add 100 widgets to the container, using the populate() function. 2 Add another 100 widgets. 3 Clear the container. The code for the stressTest function is provided here: function stressTest(){ for (var i=0;i<240;i++){ populate (100); populate(100); container.clear(); } alert("done"); } Licensed to jonathan zheng <yiyisjun@gmail.com> [...]... showing the Ajax interaction Let’s go over the first two steps, which happen before the Ajax request is sent to the server We’ll explain the third step (the DOM interaction with the server’s XML response document) in more detail in section 9.4, since we need to talk about the server before we can implement the client-side architecture completely 9.2.1 Designing the form The form in this example involves... look at runs E and G in the right context, it does make a small positive difference Interestingly, there is no single clear winner, with three quite different combinations all resulting in only a small increase in memory All three of these reuse Licensed to jonathan zheng Summary 323 DOM nodes, but so does the combination that results in the highest memory increase We can’t draw... suitable value for the machine on which we were testing; your mileage may vary considerably Recording the change in memory footprint was a relatively primitive business We ran the tests on the Windows operating system, keeping the Task Manager open We noted the memory consumption of iexplore.exe directly after loading the test page and then again after the alert box appeared, indicating that the test had... eliminating the need to post the entire page back to the server; instead, we are passing only the information necessary The page is not reloaded, so you do not have to worry about the scroll position of the page or what option was selected in the first drop-down field The initial page loading time is also shortened since the JavaScript arrays do not have to be included in the page This example will involve... switching on Unlink On Hide at the same time generated a threefold increase! In this particular case, the reason is understandable—because the DOM nodes have been unlinked, they can’t be found by a call to document.getElementById() in order to be reused Similarly, switching on Unlink On Hide by itself increased memory usage against the base case (comparing runs C to A) Before we discount Unlink On... The last thing to do is change the code of the server-side script to handle the multiple values passed in the request In NET, the multiple values are represented in a single string, separated by commas In order to get each item individually, we need to split the string into an array We can then build our WHERE clause for the SQL statement by looping through the array Dim strQuery As String = Request.Form("q")... made Designing the test script will be an art in itself, requiring an understanding of typical usage patterns and possibly of more than one type of usage pattern Running the stress test takes over a minute, during which time the browser doesn’t respond to user input If the number of iterations is increased, the browser may crash If too few iterations are employed, the change in memory footprint may not... lists have unique names, you will be able to have multiple combination elements on the page without having to separate all of the logic into different server-side pages 9 .6 Refactoring So what do you think is lacking at this point? I suspect I know what you’re thinking—generality This is an extremely cool, jazzed-up technique for implementing double combos, but it needs a little polish to be a generalized... about creating querystrings ■ Routing the response back to a component that knows how to handle it and performing appropriate error handling So let’s start our refactoring of net.ContentLoader, and then we’ll move on to repackaging our double combo as a component 9 .6. 1 New and improved net.ContentLoader Let’s start by thinking about how the constructor should be changed Consider the following constructor:... request to the server 9.2.2 Designing the client/server interactions The FillTerritory() function’s main purpose is to gather the information that is needed to send a request to the server This information includes the selected option from the first list, the name of the form, and the name of the second selection list With this information we can use the Ajax functions in our JavaScript library to send . footprint on a Windows machine. This window is tracking an instance of Mozilla Firefox running the stress test described in section 8.4.2. Licensed to jonathan zheng <yiyisjun@gmail.com> 3 16. processes running in the operating system and their resource use. It can usually be invoked Figure 8.5 Windows Task Manager showing running processes and their memory usage. Processes are being sorted. DIV. The DOM node retains a reference to the backing object in a variable called backingObj. A Container class is also defined that houses ClickBox objects and maintains an array of them,