Introduction to Dev Tools, v3

Memory Leaks & Memory Heap Snapshots

Jon Kuperman

Jon Kuperman

Introduction to Dev Tools, v3

Check out a free preview of the full Introduction to Dev Tools, v3 course

The "Memory Leaks & Memory Heap Snapshots" Lesson is part of the full, Introduction to Dev Tools, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Jon discusses how to recognise a JavaScript memory leak by using Chrome Task Manager, the Performance panel, and the Memory panel. The Chrome Task Manager is not part of the DevTools, but can be used to display JavaScript memory being used by Chrome tabs. A student's question regarding what to look for in the performance panel to recognise a memory leak is also covered.


Transcript from the "Memory Leaks & Memory Heap Snapshots" Lesson

>> So the last lesson around performance is going to be on memory. Another call out that I just wanted to make before we go too much further is that I can't remember exactly when I think very soon there's going to be another course coming in Frontend Masters.

It's on the workshop schedule for advanced dev tools stuff that Umar is going to be running, he does all sorts of good stuff. So if you are like these are meant to serve as kind of like your introduction into like, okay, I get what the performance tab does and I know what I'm looking at and I see paging and stuff like that but if you're interested in finding out a lot more on like, he's going to do I think real life case studies and like a bunch of really cool intricate stuff into like memory, CPU, network, all that stuff.

Stay tuned for his class because it's coming pretty soon. So yeah, the basic idea with memory. So JavaScript as a lot of people know has garbage collection to it, right? So you don't need to actually declare and purge your memory. But with garbage collected languages, you can end up with what we call memory leaks, but they're basically like stuff that you've forgotten about.

So like, one example would be if you make too many DOM nodes, and you don't clean up after them, so if you're like making a table, a lot of people use like virtual scrollers. And what those will do is they'll render like 20 frames of the table and then as you scroll, it'll reuse the top ones and replace them in the bottom, things like that so you don't end up with a huge DOM.

But if you didn't use a virtual scrolling, you just tried to render like a million table rows or something like that. It would take a lot of memory and you'd start noticing a real performance hit after a while. Another thing that can get you is, this one's a little bit tricky.

But when you think about code, like you're often interacting between the DOM and JavaScript, like we've been doing it a lot today. So for example, we'll like in JavaScript will do document.create element LI right? And so we have a list item but it's in memory only it hasn't been attached to the DOM yet.

And then we'll do that LI that we did, we'll do like body.add child and we'll pass that and now LI it's in the DOM. And so one thing that can happen is that those references to the in memory ones that you never did put in the DOM or you put in you took out if you don't know those out or delete them somehow those can just grow and grow in memory as well.

And so the kind of most important thing that you want to do is like, establish like, if you have a memory like that's like the first step right? Before we go diving too deep into it, so we really have like three tools for it. And one of them is a caveat of the course because it is in Chrome, but it is not part of the Chrome Dev Tools, but it is a very cool tool that Chrome offers.

And so that one is the Chrome Task Manager. So if you go up to the top right, then you click on the three dots, and then you go to more tools Task Manager. You get this really cool thing that I didn't know existed for a super long time.

And so this is basically like your activity manager, but just for what Chrome's doing. So it'll show all the tabs that you have open. It'll show all the services that it's running everything like that. Now, it doesn't work right off the bat because it just shows its memory footprint and its CPU.

But what we really wanna know is do I have a JavaScript memory leak, so we can right click on the headers here, and we can go over to JavaScript memory and we can add that new column here. And so now we've got JavaScript memory so we can see that the right now my front end masters tab with the chat is taking up a lot of memory.

And then my Google search is taking up some memory. My tab is taking up less memory, those kind of things. So one thing that's cool, let me kind of reorganize stuff a little bit here. So I'll close these just to kind of simplify stuff. And then I'll drag, let me grab hold of my browser here.

I'll drag this over, and then I'll kind of set this up. So what I want to be able to do is kind of do both at the same time, right, I want to see my tabs. And I also want to see the memory usage. So if we take like this tab here, it's using two megabytes of JavaScript memory basically.

And then I made all these buttons that make a bunch of DOM nodes or make a bunch of detach nodes or something like that. So we see that it's at 2700. And as I start kind of clicking on it takes like a second or two, we can see it spike up really high right to 6000.

I know that's really small. I can't find a way to make the text bigger. But this is like one cool thing you can do. So to be a memory leak, you would open up the task manager, enable JavaScript memory, highlight the tab that you're worried about. And then do whatever you're worried about.

Are you worried that it has a memory leak is it just sits there, are you worried about that has a memory leak as users interact, do whatever it is, and kind of keep track of this because ideally, all the memory that we allocate, we should also be purging, right?

So if we're creating DOM, we should destroy DOM. If we're creating a big array, we should get rid of that big array, those kinds of things. So you should always be able to find it that way. Another way that you should be able to find it, let me fullscreen this again and refresh, is actually back on the Performance tab from the CPU one.

One of the reasons I think that it seems so confusing is because it's just doing so much, but when we're on the Performance tab, we can click on memory here. So if we do a recording, and let's say we go back to our page and we do some stuff and clicking on these buttons that I know use a memory then I stop it.

We can see down here these kind of gains in memory and so memory usage like if it goes up and over and back down is never a bad thing because it's there for us to use, that's totally fine. What you always want to look for is this kind of jagged, like sawtooth.

If it's like climbing, that means it's seeing kind of using something, it's not purging, and then it's using more and more. So you can see this kind of pattern every time I click the button, it consumes more memory. It also gives us kind of a nice breakdown. So it'll show us what memory is used by the JavaScript heap, what of it is documents, what of it is like DOM nodes being created, event listeners or GPU memory being used.

So that can kind of start getting us in the right direction. We use the task manager to see if we have a leak, just watching the JavaScript memory. And then we can use the performance profile to see what type of thing is causing this issue. And then the third thing we can use is, of course, the memory panel itself.

So let me dock these back to the bottom. So we've got three options here for viewing memory. One of them is just to grab a heap snapshot. So it just grabs everything that's in memory at the time and shows it to you. So for example, I could do a snapshot like this, it takes a second and it shows this big list of items.

So this list we're going to be looking at in a couple of different ways, but the basic idea is that we'll see the type of item like what it was constructed by like a UL list item or just an internal system stuff or an array, things that should be familiar and then it'll show these couple of things that show shallow size, distance, and retain size.

Distance for the most part we can ignore. It means distance from the window object, from the global, basically like how far deep it is. But shallow and retain size are really important. So the shallow size is like, how big is that thing? So if you have an array of a million, how big is an array of a million?

Retain size is important because of how garbage collection works. So garbage collection works via this mark and sweep process. I don't know if anybody's ever heard of mark and sweep before. Basically it grabs everything that there is and it gives it like a flag like false. And then it starts from the window and it walks every single thing that the window is pointing towards and everything it's pointing towards and everything all the way down the tree.

And everything it touches, it flips the false to a true. So it's like I've seen you, I've seen you. And then at the end of the pass, anything that still has a false flag can be deleted, right? Because nothing's pointing to it. And so the idea again being if you have an array of a million and you have a function called foo, and all food does is it points at that array of a million.

So it's like array equals another big array of a million, then array of a million is the big thing, but since foo is pointing to the array of a million, the array of a million will stay as long as foo has something pointing to it. So it kind of like it's basically you can have like an object, and the object can be pointing to a key that has an array that's really big.

As long as that object stays in memory, it has to keep everything the object points to in memory too, because it doesn't know when those could be accessed. And so what can happen, with these DOM nodes that I talked about earlier, as you can accidentally still have a reference to something that you don't care about anymore.

So like a good thing would be like you create the LI, you stick it in the DOM, but you still have that LI. If you never null it out, it'll just stay in memory like forever basically. And so, that's all a long winded way of saying that you'll get two different things, one of the shallow size is the actual size of the thing, the retained size is how much memory could be free if that thing were deleted.

And the difference being like you could have an object with just two keys on it so very small shallow size, but one of those keys points to a gigantic array of like a million so the retain size is very big, because the only reason that array is sticking around is because of your object.

Does that make sense, I'm gonna pause for a second on that. So that's like basically as long as something has a pointer, it stays in memory. So if you add anything to the window object, as long as the window object stays, which it always will, that thing stays.

And so you can kind of get this difference in like how big is the thing? Okay, but how much memory kind of clear up if I deleted that thing, meaning that thing and anything that it's the only pointer to, so yeah, back onto the memory pad. So we've got the heap snapshot, which is great.

Another tool that we have, which is really useful is allocation over time. So we can basically see when memory is being allocated over a period of time, sort of like the performance or we're measuring across time. So like you could demo this pretty easily by starting a time allocation, starting record and then hitting one of my buttons and will see that every time I hit a button a whole bunch of memory is allocated.

And we can also see that it never goes back down. It's not like a shape like that. And so if I stop my recording here, I can see, let me make this a little bigger. That things are getting bigger. I can sort by shallow size I can see.

So the system in parentheses that's like its own stuff. But I can see that these arrays are really big and they're full of these detached HTMLLIElements. So if I were to look into the code just so we can kind of get a picture of what we're doing that's causing this it shouldn't be too much of a surprise that we're making a whole bunch of UILIElements and we're never knowing them out.

So like we're attaching them but we're not getting rid of them in memory, they just stay in memory. Is that kind of making sense? So I know it's contrived, like a for loop of 1000, where you're creating elements or whatever, but the idea being that you can kind of start in the memory panel, see if you have a leak or how much is being used.

You can also see what's taking it up over time. Cool. So the last thing, which was probably like the most common question I got, last time I did the course. But Chrome has added a really cool feature for it is like, people would always ask, okay, like I see I have a leak.

I know that there's a problem. How do I figure out what it is, like what's causing it? Where's it coming from, because you have these big complicated apps. And so chrome added this really cool thing, this allocation sampling. And so what this does is it lets you kind of record again over time down here, but what it's going to do is instead of, just let me go ahead and stop that, it's coming up.

Let me delete this, let me start one and then do something that takes up memory. I think the problem I keep running into is, if no memory is being used, then the profiles will be empty. And this is just a flat page. So start one, click one of the buttons.

Now this is actually going to tell you instead of just all there's a bunch of arrays, or there's a bunch of ul li elements, it's going to tell you what function is causing that problem. This is like great. It's like something I've been waiting for years and years because a lot of times complicated apps are like, yeah, I see that I have event listeners.

And there's a bunch of detached event listeners, but I don't know what's creating them or something like that. So being able to go from memory allocation to actually pinpointing you in the sources panel which function is causing things is like a superpower. It's really convenient. So yeah, I think the kind of TLDR with memory is the first way to do it is get the task manager to even see if you have a problem.

Like is it allocating a lot of memory, and if so, is it going up over time? If it does having a problem, then I would go into the Performance tab and do a performance recording and then I'll get you a little bit of like a visual on like what type of problem is it?

Is it like JS heap? Is it DOM nodes? Is an event listeners? That kind of thing. And then coming in here and actually taking a look at like, what's allocating? How much is it eventually going for the allocations snapshot so you can see what JavaScript is causing those things.

It won't always be this clear obviously, usually you'll have a couple different things that are big and taking up room. But this can really help you like narrow down what's causing stuff and what's not going away. One thing I forgot to mention that you can do too, is there's a forced garbage collection button here.

So another thing that would probably be useful because sometimes you allocate memory for a long time, but you do get rid of it eventually when garbage collection happens. So for example, if we go back to the heap snapshot, we can start a recording. And then we can like generate and then we can try to garbage collect and we should see it go back down to zero, but since it's not, that's like a way we can be sure that there is a leak, right that there's not stuff.

We're not using it and garbage collection doesn't take it away. That's a problem. Yeah, so the question is when you're looking at memory in the Performance tabs, you're looking at kind of the chart at the bottom, are you looking to see if the JavaScript heap goes up and down or if any of them are not going down?

And it's really if any of them are jigsawing up, it doesn't matter which one, then that's some sort of problem because they should all go back down to a stable level after any big allocation should always go back down. Or at the very least, it should go up and go flat.

Like, let's say you have an app where you just rendering like 10,000 things and that's just what you're doing. So it should go up and then it should stay, but it should not keep climbing like the only reason it would be climbing is if it was miss properly allocated or it's just not being garbage collected.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now