3

I have a page that contains many script components (50+) and I am getting an error when using IE at some random instance (doesn't happen in Chrome or Firefox).

"Out of Memory at line: 1"

I've done some google search too and that reveals issues with IE handling things differently to Chrome and FF. I would like to catch this error and know exactly what the cause of that script error is.

What would be the best way to use a global try-catch block on that many script components? All these script components are on the same page. Looking forward to your suggestions.

Pengyy
  • 37,383
  • 15
  • 83
  • 73
Neophile
  • 5,660
  • 14
  • 61
  • 107
  • 1
    It sounds more likely that one of these many components has a memory leak, and that's quite hard to `catch` – Bergi Mar 27 '17 at 11:53

3 Answers3

3

You might want to try window.onerror as a starting point. It will need to be added before the <script> tags that load the components.

https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror

If that fails, you might try reducing the components loaded by half until the error no longer occurs. Then, profile the page (you may have to reduce further due to the demand of profiling). Look for a memory leak as @Bergi suggested. If there is in fact a leak, it will likely occur in all browsers, so you can trouble-shoot in Chrome, as well.

If that still fails to yield anything interesting, the issue may be in one particular component that was not in the set of components you were loading. Ideally, anytime that component is included you see the issue. You could repeatedly bisect the loaded components until you isolate the culprit.

Finally, forgot to mention, your home-base for all of this should be the browser's developer tools, e.g. Chrome dev tools, or if it is unique to Edge, Edge debugger.

And FYI, Edge is the browser that crashes, but that does not mean the issue is not present in Chrome or FF.

tiffon
  • 5,040
  • 25
  • 34
  • 2
    I agree that most probably "Edge is the browser that crashes, but that does not mean the issue is not present in Chrome or FF.". And if this is the case, you may use (IMHO) superior Chrome's DevTools to track down your memory issues. – SergGr Apr 02 '17 at 02:54
2

One important thing that is missing in your question is if the error happens during the page loading or initialization or if it happens after some time while you browse the page.

If it's during loading or initialization, it's probably caused by the fact that your page contains too many components and uses much more memory than the browser is willing to accept (and IE is simply the first one to give up).

In such case there is no helping but reduce the page size. One possible way is to create only objects (components) that are currently visible (in viewport) and as soon as they get out of the viewport remove them from JS and DOM again (replacing the with empty DIVs sized to the size of the components).


In case the error happens while browsing the page, it may be caused by a memory leak. You may use Process Explorer to watch the memory used by your browser and check if the memory constantly increase - which would indicate the memory leak.

Memory leak in Internet Explorer may happen because it contains 2 separate garbage collectors (aka GC): one for DOM objects and other for JS properties. Other browsers (FF, Webkit, Chromium, etc.; not sure about the Edge) contains only one GC for both DOM and JS.

So when you create circular reference between DOM object and JS object, IE's GC cannot correctly release the memory and creates a memory leak.

var myGlobalObject;

function SetupLeak()
{
  myGlobalObject = document.getElementById("LeakDiv");
  document.getElementById("LeakDiv").expandoProperty = myGlobalObject;

  //When reference is not required anymore, make sure to release it
  myGlobalObject = null;
}

After this code it seems the LeakDiv reference was freed but LeakDiv still reference the myGlobalObject in its expandoProperty which in turn reference the LeakDiv. In other browsers their GC can recognize such situation and release both myGlobalObject and LeakDiv but IE's GCs cannot because they don't know if the referenced object is still in use or not (because it's the other GC's responsibility).

Even less visible is a circular reference created by a closure:

function SetupLeak()
{
    // The leak happens all at once
    AttachEvents( document.getElementById("LeakedDiv"));
}

function AttachEvents(element)
{
    //attach event to the element
    element.attachEvent("onclick", function {
            element.style.display = 'none';
    });
}

In this case the LeakedDiv's onclick property references the handler function whose closure element property reference the LeakedDiv.

To fix these situations you need to properly remove all references between DOM objects and JS variables:

function FreeLeak()
{
    myGlobalObject = null;
    document.getElementById("LeakDiv").expandoProperty = null;
}

And you may want to reduce (or remove completely) closures created on DOM elements:

function SetupLeak()
{
    // There is no leak anymore
    AttachEvents( "LeakedDiv" );
}

function AttachEvents(element)
{
    //attach event to the element
    document.getElementById(element).attachEvent("onclick", function {
            document.getElementById(element).style.display = 'none';
    });
}

In both cases using try-catch is not the option because the Out of memory may happen on random places in code and even if you find one line of code where it's happened the next time it may be elsewhere. The Process Explorer is the best chance to find the situations when the memory increase and and trying to guess what may be causing it.

For example: if the memory increase every time you open and close the menu (if you have one) then you should look how it's being opened and closed and look for the situations described above.

Radek Pech
  • 3,032
  • 1
  • 24
  • 29
1

You could check your localStorage before and after any components called.

Something like:

function getLocalStorage() {
    return JSON.stringify(localStorage).length;
}

function addScript(src, log) {
    if(log){
        console.log("Adding " + src + ", local storage size: " + getLocalStorage());
    }
    var s = document.createElement( 'script' );
    s.setAttribute( 'src', src );
    document.body.appendChild( s );
}

function callFunction(func, log){
    if(log){
        console.log("Calling " + func.name + ", local storage size: " + getLocalStorage());
    }
    func();
}

try {
    addScript(src1, true);
    addScript(src2, true);
    callFunction(func1, true);
    callFunction(func2, true);
}
catch(err) {
    console.log(err.message);
}

I hope it helps you. Bye.

Alessandro
  • 4,382
  • 8
  • 36
  • 70