Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript memory leaks: why would assigning object to null work?

Can someone scratch an itch for me in regards to the nature of assignment-to-null fix used to prevent memory leaks?

We are all familiar with the following technique to stop the circular reference between the DOM object and the JS object, in order to prevent memory leaks:

    function foo() {
      var ele = document.getElementById("someParagraphId");

      ele.onclick = function() {
        //some action here
      };

      ele = null;
    }

The question is why would the above work? Setting "ele" to null will definitely stop the circular references, but wouldn't it also prevent future references to "ele"?

    function foo() {
      var ele = document.getElementById("someParagraphId");

          ele.onclick = function() {
              console.log("accessing ele ... after set to null " + ele.onclick);
          };

      ele = null;

    }

And yet the event listener fires. It will complain that "ele" object is null (which is what we would expect).

Given the above behavior, are we right to deduce that the Javascript engine implemention will hold some sort of internal reference to the event listener, and it is this reference that is called when the event is triggered?

eventHandlerRef = //assignment to "ele.onclick" handler/listener;

If there were to be a reference like the above, wouldn't the assignment-to-null fix be implementation dependent? Or, is it part of the ECMAScript specification.

From my understanding, this fix has always been cross browser safe. I haven't come across many examples that makes specific mentions on detecting/sniffing the browser type before applying the null assignment.

===============EDIT==================

I think due to the way I've laid out the question may have unwittingly directly the discussion from what I was trying to convey. A couple of concepts that are being referenced:

object handles/object references ala:

 var obj1, obj2;
     obj1 = {foo: "bar"}; //obj1 points to the an area of the heap allocated 
                          //for the object literal.

 obj2 = obj1;     //obj2 points to the same position in the heap
                      //as obj1.

 //obj1 = null or
 obj1 = {/*another obj literal*/}  //the new object literal is created 
                                       //and allocated in some other part 
                                       //in the heap, and obj1 now points 
                                       //to the new location.

//obj2 is unaffected by obj1's re-assignment. 

The above isn't where my itch lies, and I regret adding the line:

console.log("accessing ele ... after set to null " + ele.onclick);

The above makes this look a closure question. I fully expected the error to be thrown as indicated in the original post.

My itch is more along the context of ... for some reason, in my mind, I keep thinking that Javascript engine will call the ele.onlick() directly when the event fires and setting the element to null is akin to the following flow:

var obj = {};
obj.method = function() {};

obj = null;
//error calling obj.method(), i.e., obj is null

Given that we know in the original post that the event handler still fires after ele has been set to null, in my mind the flow is more akin to:

var obj = {};
obj.method = function() {};

var objRef = obj; 

obj = null;  

//The Javascript Engine calling objRef.method when event triggered.

My itch comes down to the question, is the above how most Javascript implementation work, where some internal reference is pointing to the event handler assigned, and it is this internal reference is called when the event is triggered?

Or phrased differently, what is stopping a Javascript Engine implementation from calling an ele.onclick() directly (setting aside for the moment the design and architecture issues)?

Maybe my thought processes works differently, but didn't anyone else take a second look when they encountered the assignment-to-null fix for the first time, where the element reference was null-ed and yet the code for the handler was still be executed?

like image 992
SZR Avatar asked Jun 13 '11 04:06

SZR


People also ask

Can JavaScript cause memory leak?

Memory leaks can and do happen in garbage collected languages such as JavaScript. These can go unnoticed for some time, and eventually they will wreak havoc.

How memory leak happens in JavaScript?

The main cause of memory leaks in an application is due to unwanted references. The garbage collector finds the memory that is no longer in use by the program and releases it back to the operating system for further allocation.

What is the main cause of memory leaks?

DEFINITION A memory leak is the gradual deterioration of system performance that occurs over time as the result of the fragmentation of a computer's RAM due to poorly designed or programmed applications that fail to free up memory segments when they are no longer needed.

Which of the following can be done to reduce memory leakage?

Use reference objects to avoid memory leaks Using the java. lang. ref package, you can work with the garbage collector in your program. This allows you to avoid directly referencing objects and use special reference objects that the garbage collector easily clears.


1 Answers

Trashing all the old answer, and addressing the edit :)

Let's take an easier example: textContent.

var ele = document.getElementById('foo');
ele.textContent = 'abc';
ele = null;

This code sets the textContent of #foo to abc, and discards the reference to ele. Now what happens if I ask for #foo again?...

var ele = document.getElementById('foo');
ele.textContent = 'abc';
ele = null;
ele = document.getElementById('foo');
console.log('#foo is ' + ele.textContent);

It will log "#foo is abc". This simply indicates that #foo lives on, outside my script, and that document keeps a reference to it (since I got it back calling a method on document). It works the same with event handlers.

var ele = document.getElementById('foo');
ele.onclick = function() { console.log('abc'); };
ele = null;
ele = document.getElementById('foo');
console.log('#foo.onclick is ' + ele.onclick);

Event handlers are not a special kind of property. They're just variables on which you write references (to functions). These are roughly functional language features, where functions can be used as simple values. The JavaScript implementation simply calls event handlers from the DOM reference, instead of from the ele reference. Let's make another simpler example, without document.getElementById.

var a = {};
var b = a;
b.foo = function() { console.log("hello world!"); };
console.log(a.foo + " is the same as " + b.foo);
b = null;
console.log("Even though b is " + b + ", a.foo is still " + a.foo);
a.foo();

As you can see, functions are not tied to the references on which we assign them: they're tied to the objects the references point to. You can call them from any reference to the object, not just from the one you've used to assign the function.

As such, you can nullify references to break circular dependencies without affecting the correct behavior of objects.

like image 89
zneak Avatar answered Sep 21 '22 11:09

zneak