Applies to Pega Platform versions 7.2 through 8.3.1
What is memory leak?
Memory leaks can be defined as memory that is no longer required by an application and that, for some reason, is not returned to the operating system or the pool of free memory.
Creating objects and variables in the code consumes memory. JavaScript is smart enough to figure out when the application will not need the variable or object anymore and will clear it out to save memory. A memory leak occurs when the application might no longer need an object, but the JavaScript Runtime still thinks that the application does.
Symptoms
Errors
Explanations and Solutions
IFrame leak
Accidental global variables
Detached DOM nodes
Event listeners
Duplicated JavaScript includes in <HEAD>
Tools for detecting and debugging memory leaks
Chrome Task Manager
Pega JS Memory Leak Detector
Chrome Developer Tools
Symptoms
The primary symptom of a memory leak is when the performance of an application progressively worsens. A bug in the code causes the application to use more and more memory over time. For example, application users creating new work items or performing actions on existing work items continuously observe a gradual slowdown of application performance.
Also, because memory leaks lead to the diminished performance of the application by reducing the amount of memory available for it to perform tasks, browser crashes or screen freezes can eventually occur.
Errors
Typically, JavaScript memory leaks do not cause any error on screen or in the console. The symptoms mentioned in the previous section indicate possible memory leaks in the application.
Explanations and Solutions
JavaScript memory leaks are not caused by invalid code but rather by a logical flaw in the code. The following examples are JavaScript patterns that have historically contributed to the creation of memory leaks. Recognizing these patterns can help you avoid memory leaks. If you discover a memory leak, you know what to look for and correct in the JavaScript code of your application.
IFrame leak
In web applications, IFrames display multiple documents. If the top document holds any reference to an object inside the IFrame, even after the work object is closed, this can cause a memory leak. If an object outside of the IFrame refers to an object inside the IFrame, then the IFrame cannot be garbage collected. This leak mainly happens even after removing an IFrame.
Here are some patterns of memory leaks caused by IFrames:
- The outer window has a reference to an object in the inner window from within the IFrame.
window.top.innerObject = someInsideObject;
- There is a reference to an object in the inner window from the outside.
innerObject = iframeEl.contentWindow.someInsideObject;
- An event listener is added from the inner scope to the outer scope.
window.top.document.addEventLister('click',function() {...});
- jQuery methods spawning from parent window to child are coupled with a pseudo selector.
$(window.top.document).find("iframe:not(:visible)");
Solution
Try to avoid inner document reference in the outer window. If needed, during Iframe unload, clean up the inner document references in outer window.
Accidental global variables
A reference to an undeclared variable creates a new variable inside the global object. In the case of browsers, the global object is window.
function example1(arg) {
example2 = "this is a hidden global variable";
}
If example2 is supposed to hold a reference to a variable only inside the scope of the function and you forget to use var to declare it, an unexpected global variable is created.
Initializing multiple variables at the same time can also lead to accident global variables.
var a = b = 10;//b becomes a global variable.
Solution
Avoid referring to undeclared variables and initializing multiple variables at the same time.
Detached DOM nodes
Ensure that DOM references in JavaScript are nullified after the DOM nodes are removed from the document. A DOM node can only be garbage collected when there are no references to it either from the page's DOM tree or JavaScript code.
var detachedNodes:
function create() {
var ul = document.createElement('ul');
for (var i = 0; i < 10; i++) {
var li = document.createElement('li');
ul.appendChild(li);
}
detachedNodes = ul;
}
document.getElementById('create').addEventListener('click'),create);
Clicking the button referenced in the code sample above creates a <UL> node with 10 <LI> children. These nodes are referenced by the code, but they do not exist in the DOM tree, which makes them detached DOM nodes.
Solution
Nullifying detachedNodes after use makes the node eligible for garbage collection.
Event listeners
Memory leaks can be introduced if event listeners are not appropriately cleaned up when the DOM element that the event listener is being attached to is removed. Not removing event listeners can cause the listener to hang onto a reference of the element it was attached to.
Solution
Ensure that events are attached and detached using the same set of arguments.
Duplicated JavaScript includes in <HEAD>
If you are writing a custom control or HTML fragment that includes JavaScript, that JavaScript often gets added to the <HEAD> of the document. If that custom control or HTML fragment is included inside of a work object that will cause the JavaScript to get added to the <HEAD> every time the control or HTML fragment is loaded.
Solution
To avoid this condition, wrap the JavaScript includes method or JavaScript in a <pega:onlyonce> JSP tag.
Tools for detecting and debugging memory leaks
Memory leaks are, in most cases, not browser specific but related to the way browsers are implemented. You can see the problem predominantly in one browser, while another browser is significantly less affected. For many reported cases, Internet Explorer seems to easily experience performance problems caused by memory leaks. However, Microsoft does not provide an effective tool for debugging Internet Explorer to detect memory leaks. Therefore, for deeper memory leak analysis, you should use the tools available for Google Chrome in addition to the Pega JS Memory leak detector.
Use the tools in this sequence:
Chrome Task Manager
Use the Chrome Task Manager as a starting point to your memory issue investigation. The Chrome Task Manager is a real-time monitor whose columns Memory footprint and JavaScript memory tell you how much memory a page is currently using.
- From the Chrome menu, click More tools > Task Manager to open the Task Manager.
The Memory footprint column should be visible by default. - Right-click the Task column header to display its context menu.
- Click JavaScript memory to add this option as a visible column header to the Task Manager.
The two columns, Memory footprint and JavaScript memory, give you different information about how your page is using memory.
- The Memory footprint column represents native memory.
DOM nodes are stored in native memory. If this value is increasing, DOM nodes are being created.
- The JavaScript memory column represents the JavaScript (JS) heap.
This column contains two values. The value of importance is the live number (the number in parentheses). The live number represents how much memory the reachable objects on your page are using. If this number is increasing, either new objects are being created, or the existing objects are growing.
If you observe any unreasonable increase in these two memory sizes over time, consider this to be a symptom of a probable memory leak and investigate further using the Pega JS Memory Leak Detector and the Chrome Developer Tools.
Pega JS Memory Leak Detector
The memory leak detector tool analyzes the leaks in Pega Applications. This tools scans through all the objects and their properties recursively in a window object. Then it runs the properties against certain patterns to identify leaks.
Use this tool to detect different JavaScript leaks:
- Dangling Window references (IFrame or pop-up window)
This determines if there are any references to closed IFrame or pop-up windows. If there are any references to a dangling window, then that window will never get cleaned up.
- Dangling Foreign Window Object references
This determines any references to closed IFrame window properties. This also causes holding of the IFrame window.
- Orphan DOM Nodes
This determines the references to detached DOM nodes.
- Duplicate Script files
This determines if there are any duplicate script files loaded in the document.
- XML Nodes
This identifies the XMLDocument instances that are there in the memory.
Usage
Run the following statement from the browser console that is available as part of developer tools.
pega.ui.memory.scan();
Sample Result
Result Parameters
- Destroyed window references: These are the destroyed IFrame or popup window references.
Severity: Error
- Destroyed window object instances: These are the member references belonging to a destroyed window.
Severity: Error
- Orphan DOM Nodes: These are references to orphan DOM nodes or nodes created in memory and never attached to DOM.
Severity: Warning
- Duplicate Script Files Loaded: This lists the duplicate scripts loaded in the Document.
Severity: Error
- XML Nodes: Contains references to dangling XML Documents.
Severity: Warning
Chrome Developer Tools
Continue your root cause analysis of memory leaks using the Chrome Developer Tools.
From the Chrome menu, click More tools > Developer Tools to view the tabs related to this effort:
Important Tip
During debugging, you first focus on Memory Heal Snapshot and search for Detached to detect detached DOM nodes, subtrees, and windows. To ensure that you accurately record and detect Detached DOM trees, refer to this article:
How to Record Heap Snapshots, which includes Uncover DOM Leaks.
Performance
The Performance tab tells you know how much memory your page is using over a period of time.
When you are performing memory leak analysis, always be sure to select the Memory checkbox while recording an interaction using the Performance tab.
Always start and end your recording with a forced garbage collection: Click the Collect Garbage icon (next to the Memory checkbox) while recording to force garbage collection.
Memory > Heap Snapshots
Under the Memory tab, Heap snapshots provide information about memory distribution of the JavaScript objects and DOM nodes at a specific point in time.
- Use Snapshots to detect detached DOM nodes.
- Use the Class filter search field to filter the snapshot information for detached DOM nodes.
Memory > Allocation instrumentation on timeline
Under the Memory tab, Allocation instrumentation on timeline shows instrumented JavaScript memory allocations over time. Once the profile is recorded you can select a time interval to see objects that were allocated within it and still alive by the end of recording.