Skip to content

Instantly share code, notes, and snippets.

@karmadude
Last active August 29, 2015 14:21
Show Gist options
  • Save karmadude/a7e1b648682508adb781 to your computer and use it in GitHub Desktop.
Save karmadude/a7e1b648682508adb781 to your computer and use it in GitHub Desktop.
How to find JavaScript memory leaks using heap snapshot

How to find JavaScript memory leaks using heap snapshot

  1. Open js-memory-leak.html in Chrome
  2. Open developer tools and select Profiles tab
  3. Select Take Heap Snapshot Take Heap Snapshot
  4. Take a base snapshot Take base snapshot
  5. Click Create App five times
  6. Take another snapshot
  7. Type in App in the Class Filter
  8. Notice There are 5 App objects, when there should be just one. Take base snapshot
  9. To find what's causing App objects from being garbage collected, expand App to see the app instances. Take base snapshot
  10. Select any app instance without the yellow background to see the objects retaining the app instance.
  11. You can see that the app is being retained by an AppEventHandler object. Take base snapshot
  12. Now refresh the page, and check Clear App
  13. Again repeat steps 3-7.
  14. You will now notice that the memory leak has been fixed and there is only one App object, regardless of how many times you try to create an app.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px/16px Helvetica, sans-serif;
}
main {
width: 720px;
margin: 50px auto;
}
.app {
cursor: pointer;
background: #9ddcd7;
border: 1px solid #92CDC8;
color: #fff;
width: 100%;
height: 48px;
margin: 20px 0;
font-size: 20px;
font-weight: 500;
line-height: 48px;
text-align: center;
}
</style>
<main>
<div id="test-app"></div>
<button id="create-app">Create App</button>
<input type="checkbox" id="clear-app"> <label>Clear App</label>
| <label>Leaked: <span id="leaked-app-count">0</span></label>
</main>
<script>
var appCount = 0;
var app;
var createAppButton = document.getElementById("create-app");
var clearAppCheckbox = document.getElementById("clear-app");
var leakedAppCountSpan = document.getElementById("leaked-app-count");
createAppButton.onclick = createApp;
function App(id) {
this.id = id;
this.el = document.getElementById("test-app");
this.el.className = "app";
this.el.textContent = "App " + this.id;
this.handler = new AppEventHandler(this);
console.log("App " + this.id + " created.");
}
App.prototype.remove = function(event) {
this.el.className = "";
this.handler.remove();
console.log("App " + this.id + " removed.");
};
function AppEventHandler(app) {
this.app = app;
app.el.addEventListener("click", this);
}
AppEventHandler.prototype.handleEvent = function(event) {
console.log(event.type + "ed app " + this.app.id);
};
AppEventHandler.prototype.remove = function(event) {
this.app.el.removeEventListener("click", this);
};
function createApp() {
if(clearAppCheckbox.checked)
clearApp();
leakedAppCountSpan.textContent = appCount;
app = new App(appCount+1);
appCount++;
}
function clearApp() {
if(!app)
return;
app.remove();
app = null;
appCount = appCount === 0 ? appCount : appCount - 1;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment