Microsoft on why a memory leak isn’t really a leak

The genesis of this rant is that a colleague and I have just spent a couple of days diagnosing and fixing memory leaks (sorry, “pseudo-leaks”,” according to Microsoft, which presumably means that the memory explosion we were seeing wasn’t actually real) caused by awful, awful garbage collection in Internet Explorer.

The icing on the cake was finding this article: Understanding and Solving Internet Explorer Leak Patterns. The markworthy text is this:

Pseudo-leaks almost always appear on the same page during dynamic scripting operations and should rarely be visible after navigation away from the page to a blank page.

In other words, Microsoft Word doesn’t leak memory either. All you have to do is close it, open it again and miraculously all the memory that it allocated and failed to release (but that somehow fails to meet the definition of “leaked”) is released.

The whole point of Internet Exploder 8 was to build an AJAX-friendly browser. The subtext went along the lines of, “Well, all those AJAX-heavy sites like GMail, Google Maps, Hotmail etc don’t perform well under IE7 and we’re losing browser market share, so let’s make a browser that is AJAX-friendly. But, at the same time, let’s make developers require people to navigate away from that application before any of its memory is released.”

We’ve just spent days diagnosing as many of the various ways that IE leaks memory (ways, incidentally, with which none of the other browsers seem to have problems), patching the jQuery core to cope with its idiocy and writing our own DOM garbage collection handlers to deal with it. The jQuery and GWT discussion forums reveal that these guys are having just as much pain, and for similar reasons.

To the IE development team: Please, please, please, guys, fix your sodding .removeChild method and everything that has anything to do with it. And don’t talk to me about setting .innerHTML properties either until you have a browser that doesn’t seg-fault when I do that to a table element, or when I manually break the relationship between a node and its parent. Finally, at least have the courage to confess that your browser does leak memory in these scenarios rather than some pathetic attempt at explaining why a leak isn’t a leak. Grr!!

If you want to cringe, grab the sIEve tool (see Memory leak detector for Internet Explorer for a link) and point it at http://msdn.microsoft.com. Navigate around a bit and then have a look at the number of orphan DOM nodes. Consider how many full-page reloads MSDN causes, and compare this to your own AJAX application. Weep.

Memory leak detector for Internet Explorer

I’ve been playing with Drip and sIEve in order to find some memory leaks that we’ve been encountering under Internet Exploder.

Drip / IESieve, Memory leak detector for IE Internet Explorer .

If you haven’t looked at your application with sIEve, you really should.

console.log() Equivalent for Internet Explorer

There are a bunch of people out there who are fed up with the lack of a console.log() equivalent in Internet Explorer. It shouldn’t come as any surprise that I’m one of them.

For anyone who’s ever tried to debug a whole bunch of JavaScript code and ended up with myriad alert(‘here’) and alert(‘here2’) calls just so they could see what was happening, console.log() became our friend very, very quickly. No surprise, however, that IE didn’t have it.

It becomes significantly more painful, however, when you’re trying to clean up JS code for the sake of performance. The usefulness of any metrics collected goes out the window once there’s user activity involved. (Besides, clicking “OK” for all those alert boxes is a royal pain.)

(Hint to the IE8 team: Your product is still in beta. You must have a logging call somewhere. Publish it, please. Please. All the other browsers of note do.)

There are quite a few good console.log() equivalents out there, not the least of which are Faux Console and the Yahoo User Interface Logger Widget. For extremely light-weight applications, though, there was nothing that did just what I wanted, so I wrote one. You’ll be depressed at how simple it is, and how easy it would have been for the IE team to have included this functionality at almost any point in IE’s development cycle.

 

The JavaScript code:

// rudimentary javascript logging to emulate console.log(). If there already exists
// an object named "console" (defined by most *useful* browsers :p) then we
// won't do anything here at all.
if (typeof (console) === 'undefined') {

    // define "console" namespace
    console = new function() {
        // this is the Id of the console div. It doesn't actually need to be a div,
        // as long as it has an innerHTML property.
        this.ConsoleDivId = "JavaScriptConsole";

        // maintains a reference to the console output div, so that we don't have to
        // call document.getElementById a bunch of times.
        this.ConsoleDiv = null;

        // allows us to cache whether or not the console div exists, so that we can
        // just do an early exit from the console.log method and similar if we're not
        // going to put any useful output anywhere.
        this.ConsoleDivExists = null;
    };

    // this is an expensive (really quite expensive) string padding function. Don't use
    // it for large strings.  -andrewh 11/3/09
    console.padString = function(s, padToLength, padCharacter) {
        var response = "" + s;
        while (response.length < padToLength) {
            response = padCharacter + response;
        }

        return response;
    }

    console.log = function(message) {

        // this will be executed once, on first method invocation, to get a reference to the
        // output div if it exists
        if (console.ConsoleDivExists == null) {
            console.ConsoleDiv = document.getElementById(console.ConsoleDivId);
            console.ConsoleDivExists = (console.ConsoleDiv != null);
        }

        // only do any logging if we actually have an output div.  (Check using the cached
        // variable so that we don't end up with a bunch of failed calls to
        // document.getElementById).
        if (console.ConsoleDivExists) {
            var date = new Date();
            var entireMessage =
                console.padString(date.getHours(), 2, "0") + ":" +
                console.padString(date.getMinutes(), 2, "0") + ":" +
                console.padString(date.getSeconds(), 2, "0") + "." +
                console.padString(date.getMilliseconds(), 3, "0") + " " + message;
            delete date;

            // append the message
            console.ConsoleDiv.innerHTML = console.ConsoleDiv.innerHTML + "<br />" + entireMessage;

            // scroll the div to the bottom
            console.ConsoleDiv.scrollTop = console.ConsoleDiv.scrollHeight;
        }
    }
}

Ideally you’d drop this into an included script file, but it’s more likely that you’ll paste it into a <script> tag in the header of your HTML document.

 

The HTML that creates the DIV to contain the output:

<!--
 This is here for JavaScript debugging. Please use calls to console.log(message) to log to this console, as
 we're emulating the console.log() function that real browsers provide.  -andrewh 11/3/09
 -->
 <div id="JavaScriptConsole" style="position: absolute; bottom: 30px; left: 30px; width: 600px; height: 200px; overflow: scroll; background-color: Yellow; color: Red;">
     <a href="javascript:document.getElementById('JavascriptConsole').style.visibility = 'hidden';" style="float: right;">Close</a> <span style="font-weight: bold;">JavaScript Console</span><br />
 </div>

Note that this div also contains a hyperlink with JavaScript code in it to hide it.

 

A simple hello world script to log to it:

<script type="text/javascript">
   1: console.log("Hello, world!");

</script>

 

… and finally, the output:

image

Microsoft’s Azure Services Platform

… and why you really should care.

I’m sitting in a Microsoft user group meeting right now, and I am, to be honest, pretty unimpressed. Not with the presenters – they’re doing a good job – or with the presentation, which is on what should be a fascinating topic, but with the people. Sorry, Microsoft, but the greatest problem you face right now is not your technology; it’s pretty damn good[1]. It’s the people who are afraid of using it – who, sadly, aren’t very.

OK, I’m home now, so I can type properly.

The presentation topic was the Azure Services Platform, which is Microsoft’s  answer to the Google cloud. Azure is a fascinating topic, both technically and strategically. The technical merits I’ll discuss in a minute. Strategically, however, this platform shows that Microsoft is quaking in its boots over what Google’s been doing with cloud computing, and is now trying to play catch-up. The degree of success a) remains to be seen; and b) depends upon the aforementioned people who are going to have to want to learn to use and exploit its strengths.

This platform gives immeasurable advantages to whomever wants them: almost infinite scalability, massive parallelism and redundancy, no more worries about server provisioning or downtime… the list goes on.

One of the reasons I’m so irritated is that instead of asking intelligent questions like, “How much can we scale a single computational task?” or even “How does this compare to the Google cloud in terms of speed, flexibility and response time?” people asked questions around keeping their own servers (“Can I still be woken up at 3am when a server falls over, please?”) and security (”Can I host my own database and have the platform talk to it?” or, in other words, “Can I still trust Microsoft with my unencrypted data, but nonetheless re-introduce my own single point of failure into an otherwise-well-designed system?”). Honestly.

I don’t want to rant, so suffice it to say this:

Learn about your craft. Go and sign up for the Azure CTP. Go and get your Google App Engine key. Read about the Google file-system and Amazon’s S3. And, while you’re at it, go and re-read some Knuth and some Fowler[2], just because you should, and probably haven’t.

Get some enthusiasm about what you’re seeing, people. It’s brilliant. Go and learn about it. For what it’s worth, if you haven’t been hanging out for a cloud computing solution from Microsoft for a very long time, I most respectfully suggest that you might be in the wrong profession.

 

[1] Except for Live Writer. What were you thinking, guys? Writing this post has been painful. I tried to screenshot the crash messages and embed them into another blog post (also in Live Writer) and it crashed, too. Fail.

[2] Who are they? Shame on you.

#if DEBUG Considered Harmful

I know, I know. Lots of people have written about this one, but nonetheless it still gets used and I feel I should add my $0.02. (That’s Australian money, by the way, so it probably works out at not very much in your own currency.)

This post is specific to C#, as .NET has the very nice feature of code attributes, specifically the ConditionalAttribute class which allows methods to be compiled and invoked by the JIT compiler only if there’s a particular compilation variable set.

Consider the code below:

private static void Hello()
{
    Console.WriteLine("Hello, world!");
}

private static void Goodbye()
{
    Console.WriteLine("Goodbye, cruel world!");
}

public static void GreetTheWorld()
{
    #if DEBUG
    Hello();
    #endif

    Goodbye();
}

Let’s say that we compile this in Debug mode with code analysis turned on and warnings set to errors. (We all compile with warnings == errors, right?) All is well.

We go to run our unit tests again in Release mode prior to check-in, so we recompile in Release mode. (Or, if we’re lazy, we just check in from our Debug build and let our build server compile and run the tests in Release mode.)

Oops. CA1811 violation: you have uncalled private methods in your code. Please call them if you meant to call them, or remove them if not. The FxCop engine will never notice that our #if DEBUG directive has compiled out the call to our Hello() method, so code analysis throws an error.

Use this one instead:

[Conditional("DEBUG")]
private static void Hello()
{
    Console.WriteLine("Hello, world!");
}

private static void Goodbye()
{
    Console.WriteLine("Goodbye, cruel world!");
}

public static void GreetTheWorld()
{
    Hello();
    Goodbye();
}

This makes the compiler much happier.

Let’s consider the first piece of code again, though, and edit it in Release mode. Perhaps we’d like to rename our methods to something more descriptive of what they do: PrintHello() and PrintGoodbye(). So, we whip out our trusty refactoring tool (^R ^R in Visual Studio) and tell it to rename our methods.

Here’s what we end up with (remembering that we’re in Release mode):

private static void PrintHello()
{
    Console.WriteLine("Hello, world!");
}

private static void PrintGoodbye()
{
    Console.WriteLine("Goodbye, cruel world!");
}

public static void GreetTheWorld()
{
    #if DEBUG
    Hello();
    #endif

    PrintGoodbye();
}

Oh, sod. We’ve introduced a compilation error because the refactor/rename operation uses the compiled version of the code to check for symbol usage, and our call to the former Hello() method doesn’t appear in the compiled assembly because the #if DEBUG check caused it to not be compiled. We’ve left the old call to Hello() unchanged.

If we’d performed the same operation on the second piece of code instead, we’d be laughing.

Brisbane Alt.Net User Group Launched

The Brisbane Alt.Net User Group has launched. Check it out at Brisbane Alt.Net or, even better, turn up to the first meeting in February.

The Windows 7 Beta Kicks Off This Week – Windows 7 Team Blog – The Windows Blog

Just in case you missed it, the Windows 7 Beta is now available.

The Windows 7 Beta Kicks Off This Week – Windows 7 Team Blog – The Windows Blog .

As I’ve been officially on holidays for the last four days (yes, four days includes two days of weekend; /sigh) I haven’t fetched it yet so I have no wisdom for anyone about what’s good and what’s not. Why not try it and see?

Braces in string.Format()

I’m really quite surprised that I’ve never needed this before, but today I wanted to embed some JavaScript within a string contained in a C# class and format it using string.Format().

The problem? My JavaScript was a function declaration and therefore contained braces, but the placeholder delimiters in string.Format also use braces.

The solution: braces get escaped using another brace of the same sort.

string jsConditionalHelloWorldTemplate =
    "if ({0}) {{\r\n" +
    "    alert('Hello, world!');" +
    "}}" +
    "";

string sendToBrowser = string.Format(jsConditionalHelloWorldTemplate, "true")
writer.Write(sendToBrowser);

Simple, really, but not intuitive – most civilized formatters use a backslash to escape characters. Hmm.

JavaScript .cloneNode() doesn’t clone event handlers

Just for the record, I hate JavaScript. It makes me feel dirty. Nonetheless, if you’re going to use a tool – even one that you hate – use it well.

Here’s one that will one day bite you. Consider this code:

<html>
    <head>
        <script type="text/javascript">

            var OnLoad = function() {
                document.getElementById("cloneme").onclick = function() { alert("onload");};
                document.body.appendChild(document.getElementById("cloneme").cloneNode(true));
            }

            window.attachEvent("onload", OnLoad);
        </script>
    </head>
    <body>
        <a id="cloneme" href="javascript:alert('href');">Test Hyperlink</a><br />
        <br />
    </body>
</html>

If you drop it into a local .html file and point a browser at it, it’ll look something like this:

Yes, I know, I used Internet Exploder for this example. I’m sorry. I’m a bad human.

There is a catch, however: although one hyperlink is a direct clone of the other, they’re not identical.

If you click the first one, you’ll see the order in which the events should fire represented by two alert boxes, the first one shouting “onload” and the second “href”.

If you click the second one, you’ll only see the “href” message.

Why is this?

The key point to remember is that the DOM object is being cloned from its textual representation. In other words, .clone() does not do a deep copy; rather, it effectively just creates a new node based on the .outerHtml property of the old node. Notably, that does not include any event handlers that have been attached programmatically.

Microsoft JScript compilation error: ‘return’ statement outside of function

I feel dirty. Oh, so dirty.

Internet Exploder runs script evaluated using the JavaScript eval() function in the global scope.

So what?

Well, if you’re doing something silly, as I’m being obliged to do right now, such as dynamically adding and removing click handlers based on CSS attributes, fake HtmlTextWriter objects and third-party controls (over which I have no control), you’re eventually going to run into the error above.

Your code might look, in a very simplified form, something like this:

var RunCrazyUnsafeScript = function(scriptToEval) {
    eval(scriptToEval);
}

… and you’re probably invoking it something like this:

RunCrazyUnsafeScript("alert('Hello, world!');");

… and by and large, this all works perfectly well. Occasionally, however, you’ll see the error above. The odds are that you’re probably doing something like this:

RunCrazyUnsafeScript("alert('Hello, world!'); return false;");

This is perfectly legitimate in an event handler, and there are plenty of cases in which you actually want to return a value from evaluated code. I can’t understand why the JavaScript implementation of eval() doesn’t function more along the lines of perl, where eval() can be used to return all sorts of things, including – gasp! – the return value of the evaluated code. Wow.

Anyway… I’ll come to the code snippet that you can copy and paste so that you can leave this blog and go back to your own code.

 

var ScriptHelpers = function() {
    var _this = this;    // don't trust "this" in JS unless you *really* know what you're doing

    // IMPORTANT: This function is NOT thread safe. If you want to use it in a
    // multi-threaded application, you need to make it so. Or, preferably, fix
    // the code that's forcing you do use this approach at all.
    _this.EvalWithReturnValue = function(scriptToEval) {
        returnValue = undefined;
        scriptToEval = expToEval.replace(" return ", " returnValue= ");    // euww.. global. icky, icky, icky.  -andrewh 25/11/08
        eval(scriptToEval);
        return returnValue;
    }
}

var scriptWotWeGotFromSomeBlokeInAPub = "alert('Hello, world!'); return false;";
var scriptReturnValue = ScriptHelpers.EvalWithReturnValue(scriptWotWeGotFromSomeBlokeInAPub);

Next Page »