Debugging memory leaks - HTTP 203

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
already take two what happened to the one take wonders i know i know we were doing quite well at home weren't we this is a second take [Music] we're back well yeah we are in our new basement studio temporary accommodation but better than none i'd say yeah absolutely yeah and um i thought you know i'm gonna throw us into the deep end oh there's no easing in coming back from the working from home filming from home era even straight okay straight in to debugging memory leaks everyone's favorite i i think it's the hardest thing to do with devtools well how does that agree yeah just in general trying to do something like trying to figure out where where memory is going it's so much harder than you know figuring out css problems so much harder than figuring out performance problems it's it's already hard i think in garbage collected languages in general to figure out when is something being garbage collected and reason about that and now put on top that our we have tools and devtools but they just give you numbers and then go you figure it out and we're going to look at some of those numbers great i've figured out what some of them mean not all of them but some of them so what happened is uh someone on github let us know that um squoosh i know this app yeah it's the thing we built uh that we still wanted a couple more but yeah you know this is the one we still maintain so yeah but they were saying it's using a lot of memory i mean and that's images it does images and they're big so that's that was the first thing i said it's like yeah i mean it's gonna and like no no no it's using more and more memory uh that's not good classic memory leak territory yeah so the first thing i want to do is like you know can i recreate this problem so i went into uh dev tools and into the old memory tab uh how did they figure it out uh they saw it in task manager they saw the memory going up and up and up which you can use as well but then it sort of mixes in like other processes it's harder to see i could imagine it's the kind of thing where chrome sometimes grabs memory and even though the process that the renderer process health frees it back up chrome doesn't give it back because it might need it or something yep that's classically chrome behavior i might need all of your memory so i'll take it now um but yeah this is how i recreate it and we got a comment a couple of episodes ago where someone was saying look i watch your episodes on my mobile and i struggle to read some of the code can you make the code bigger sorry mate you're not going to like this one because the the tools are dense they're really really i think they're mostly glorified spreadsheets lots of numbers and so you either get big numbers but only see a third of the data is available how do you make them smaller yeah i've zoomed in as much as i can look i'll zoom in a little bit more that's as much as we can do but you'll see in these videos i'm struggling even with some of the column sizes because i've zoomed it maybe slightly too much anyway let's let's see how easy this is to understand the first thing i did is take a heap snapshot when you press that button what it does is it does uh an aggressive piece of garbage collection and then goes right finds everything that javascript has caused or so basically make sure that everything that can be deleted is deleted and then it measures how much you have on your heap because that's the part where javascript objects slip well you'll see that this there's a few icons there's a bin icon and there's a no icon and the bin icon what does that do that runs garbage collection it does yes well done whereas the no icon deletes things like you know the actual snapshots yeah traditionally the bin for throwing stuff away that's not what's happening here it's just for garbage collection but you don't actually need to press that it's done automatically okay now i know an array has a shallow size of 590 000 which is 22 percent that is very insightful yes and so it's like we're using two megabytes or 2.7 megabytes not very interesting but now i've opened an image i've taken another heap snapshot we're up to 80 megabytes that makes sense for that image i'd say like it's a do you remember the the resolution yeah like 3 000 by something three it's a big image three by two so it's like six million pixels so 24 million bytes yeah i i thought are you going to see this maps through you're going to do yeah sounds about right sounds about right excellent cool um and that's yeah it sounded about right to me like just memory going up isn't a problem we've loaded a big image and now we have a big images worth of memory being used and we actually see this on the side there what i can do is do this comparison and say hey show me the difference between snapshot one and snapshot two and we can see here there's these three array buffers 40 megabytes each well there's two of them are 40 megabytes and one of them is significantly less how how did you know to look there because there's also many other things you could have unfolded it was the biggest things there which is usually the first thing to do is there anything like it's memory's gone up a lot but is there anything big this stuff gets a lot harder if it's lots of little things thankfully for this one as memory debugging goes it was pretty easy because it's three big things so there's two gs arrays that are exactly the same size i'm assuming that is the copy of the image on the main thread and the other one from the worker because we're probably going to have two copies of the image flying around so you you raised an interesting point here is like i think if if i'm looking at performance i can just go to a random website and i can look at the the performance timeline and i can come up with some decent recommendation i would say when you're debugging memory stuff you really need some knowledge of the app yeah which is what you were talking about there so i can actually tell you so what we've done here is i've clicked into uh one of these arrays and gone into the retainers which shows you all of the uh you know the array is the the leaf node and now we're going back up things we're holding the references to that array and who's holding a reference to the thing that's calling the reference so we can see the next thing on the tree there is uh uintah clamped array and then the next thing is image data so there you go that's there's a big clue and yes um we've got these two 40 megabyte things it's decoded image data it's the one on the left which is the original image and it's the one on the right which is the encoded version then decoded and the third one is 860k which is funnily enough exactly the size of the compressed image so it's probably that one good eyes yes that is the encoded jpeg so that's the three and you know with app knowledge we can look at that and go that's fine working as intended working as intended so the next phase is to close that don't do a reload because we're looking for leaks yes so just close it within the app reload it and then do another heap snapshot uh and at that point you see oh twice the memory twice the memory and again some app knowledge comes into this because that might be fine because it might be doing some caching you might be you might have a way to go back to the previous image you've got to keep it in memory yeah maybe um but we know we don't do that we do have some caching but we know the caching is gone when you press that yeah x uh so yeah as you saw in the summary right so i guess you could do a comparison that is what i'm going to do um so yeah we can see we've got like a lot more of those array buffers than we had before we had uh three before we now have six so it's just a comparison to snapshot one or snapshot 2. so this is just the raw snapshot 3 right now oh but let's let's take a look let's have a you know compare it to snapshots oh you can select which one to compare to yes and and so we can see now that there's some stuff being created some stuff being freed but we're still ending up with more than we intended this is not really a useful view because we we know that there are these six array buffers still in memory but we really want to know well some of those we need because they're on screen like the images are on screen what we want to know is like where are the ones that probably shouldn't be around anymore and yeah i mean the fact that we have it looks like the ones at the bottom are the ones that are being freed so two 40 megabyte things were free good because of probably the previous image we didn't want those anymore and yet four forty megabytes one are being created that seems like two too many yes absolutely and so what i've done here is i'm now showing the objects allocated between snapshot one and snapshot two that are still around in snapshot three okay yeah so these are the ones that we would expect to be gone because they were created you know for that first showing of the image but they are still there for snapshot three so i didn't know this tool could do that and yeah sometimes you just want to use the the other kind of comparison but this this really narrows it down to the ones that we should be good because if we know again with that knowledge like everything created between snapshot 1 and snapshot 2 should not exist once we go back to the landing page and start over that's a big red flag absolutely yeah so now it's trying to find out where you know why are they there yeah where did they come from yes and so again we can click into it look at these retainers which shows all of the things that are holding on to that in memory we're seeing it's image data we saw that before write compressed they we know that that's a term from within squish's source code so that yeah that all makes sense and there is one red flag there in that list for me and that is detached html element so a detached html element is an html element that's no longer in the document and that isn't necessarily a bad thing you could have like a menu in your app yeah that you put in the dom and the user clicks the menu thing and then when they click away you take the dom element out but you hold on to it for later yeah but again a bit of app knowledge comes into play here i know we don't it shouldn't so this makes me think that we probably hold a reference to the storm element and one of our components states and don't hey you don't have to guess we've got tooling now oh so what i would do is try and find out which element this is and a handy way of doing that is similar to what you've seen in other parts of dev tools you can right click and go uh storage global variable and there it is and it's our two-up customer it sounds good element so that's the one that's got it shows the image either side and you swipe either side and so it's like right and again that confirms to me we don't catch those no they really shouldn't be around from a previous run yeah so something happened here that caught me out when i was debugging this for reals is at this point it is useless to do another heap snapshot and do more testing because i have just stored something as a global variable yeah and that will mess with the results same with console logs like if you're doing console logs it's going to it it's going to create memory leaks because those things have got to stay in the console and be unfoldable yeah exactly so all right let's try and find out where this where this turf is why is it there uh so again you want to sort of dig further and further down or like you know towards the root of the tree there are these internal nodes those are c functions within chrome right apparently we might get those named at some point but they're all called internal node right now which is very very unhelpful but before that there is va event listener and we know what event lessons are i'm assuming yeah and just above that is a link that we can press to some code and that takes us here one key down on key down sounds like an event listener is this all urus to tell me that a pr that i merged and reviewed introduced the memory league yep but it's okay because there's another one that i i merged that caused the same problem and we'll look at that in a second but yes we can see that here we've got a window add event listener key down uh and that is the source of our leak the problem here is is that this listener is on the window object and that's going to continue to fire even if the element is gone because it's on the window so you know we create this two-up element we take it away and when you press keys it's still firing that event and we do the trick i think where this on key down gets bound to this so we don't have to worry about retaining this it's it doesn't but it has a closure to it it's an arrow function it's an error function okay same same deal then okay so the way of fixing this and apologies this is because this is a real world case it's a real world bug squish is written in typescript apologies if you're not used to typescript but it's it's javascript it's mostly javascript uh this is our constructor for our custom element for the two-up um we're gonna go and find out where that that event listener is uh it's further down here uh in the constructor for the for the event for the for the element uh this is with custom elements this is a bad place to be putting an event listener um because what we want to do is we only need this listener when the element is in the document we should do it on it on attached callback attach callback connected callback connect to callback close it's been a while i just realized since i've written a custom element shame on me no it's i had to look this stuff up as well so i think we already have a function for that there we go connected callback so it is just moving uh the the listener there but then we need to do the reverse disconnected don't we we need disconnected callback which is when it's taken out of the document so there it goes and uh if you copy and paste a lot like i do make sure you change it to remove event listener i have created bugs where my disconnected thing is actually just adding another listener do that and that is the problem well solved uh the right thing to do is to confirm that by going back into your app hit refresh pick up the new code and then just uh do the same thing open the image heap close the image open the image heap easy as that just remember those steps but it's still twice as much memory yeah still twice as much it's because it's not fixed is it no uh it is fixed like when i first saw that i was like well either that wasn't a problem or there are more problems i can tell you now there were more problems great so we're back here again same business interesting because now the the retainer chain that just sounds like something that the dentist puts in your mouth but yeah the retainer change should be different now shouldn't it it is going to be different so we're going to look at the same array buffer and there is no detached html element there so we have sold weight problem a small win with zero effect but again we're now just doing the same thing i have a quick question then actually so this the the chain of retainers is obviously a chain here but multiple elements can be holding a reference to the same leaf can we see all of them yes you scroll down you'll see them all okay but sometimes some of them are false uh because well because some of them will have a reference to it but it would have otherwise been collected so sometimes you need to find the one that is actually the real retainer it normally does a good job of making sure it's the one at the top not always okay but again it's just looking at it and sometimes it's just finding out like where is this where is this stuff so clicking through some code this is a preact component this time not a web component looking at the constructor isn't helping me at all figure out it knows roughly where it is but it's not helping me we've got again i felt like this line wasn't very significant but it told me what component it was in but yeah it was insignificant it was just telling me it's this is an instance of this class okay um oh okay yeah further down we've got an event listener there's a bigger clue um move around for this tool tip to get out the way and again here we've got on mobile width change we've got an event listener here um again it's it's just a function so it's finding out well where is that made into an event yeah and we've got with query add listener um which when i saw this like i then knew what it was because i'm familiar with this code but uh you can chase it further add event this is add listener so this is some custom code no this is match media and match media uses uh you can use ad event listener um but it it also uses ad listener and safari doesn't support ad event listener or it didn't at some point at one point on stage in chrome dev summit the last on stage from dev summit i broke the app before going on stage and it was because i replaced one of these things from ad listeners at event listener and it didn't work in safari and everyone in the audience was like not working on my phone that was why so i just use add listener now even though you'll see it draws a line deprecated yeah because it's deprecated that's why that's happening uh with a preact component that's actually fine there in the constructor but this is the same problem as before we've got a you know width query this is something that's happening on the window object but we're using it with a component so it's keeping it alive so i just need to do what i did before this is not a custom element this is a preact component so it's a different but like callbacks exist in this one it's component will unmount remove listener job done deprecated deprecated it works it's fine and so yeah this is it just uh keep snapshot close open again mirror signal maneuver hey look at that and it's sorted and that's brilliant once i saw that it's like oh okay here we go and yes both are very similar problems and event listeners are going to be the problem it is the cliche for me there's always dangling eventlessness yeah unintentional globals or event listeners uh it tends to be so yeah that was it that was that was problem solved we shipped it excellent um but you know what we've we've got uh i would say i've got a bias towards chrome you probably do as well because it's where the money comes from that pastes the builds um but i thought i'd have a look at the similar tools in other browsers so this is firefox and it has a very similar menu uh it has a heap snapshot button there we go 18 megabytes you get this oh that's nice yeah it helps you really tell where the memory is spent very quickly this is big so we make it big yes oh there's an array buffer there's there's there's a bit of memory there's a similar listview i would say i i find these tools harder to use it could be biased but i find it really hard to find the attribution of what's holding on to objects in this especially when it comes to listeners and maps but here's here's a fun one so close it open again going to take another heap snapshot there oh no 160 megabytes well this is the fixed version this is the fixed version i thought you're going to show me walk me through this oh the whole thing no no no they're already making signals that this episode's getting a bit long so oh there's more leaks in firefox or are they do they not run some forced garbage collection they don't you have to go to about memory and press this hidden button minimize the memory usage minimize memory so basically it had twice never because the old references were still around nobody was holding on to them they are collectible but haven't been collected yet exactly and so i do that and we're back down to 80. so there's a gotcha there's something to watch out for if you're using this that makes sense uh and then finally safari in their timeline look they symbols not there at first i had to go into this this little edit menu and enable it but there's a memory line there but you you hit record and you start doing stuff and it will show you look there's some memory gone up same business again close it and open it again and oh memory is going up again so where's the forced garbage collection button ah there isn't one uh because this is a memory leak yeah so you get these little s things and they're heap snapshots you don't you don't get to choose when they happen but they always one always happens at the end which is useful um so we can dive into that here and we see something that's very similar to what we had in chrome again i find attribution a lot harder to deal with in this one versus chrome but in this case we've got a lot of canvas elements oh and it says there's a reader article something um you can click through to it and it gives you on the console uh so read article find the js elements with uh cached binding rect number 14. this is not our code no this seems like this is the reading mode i talked to a safari engineer and they went uh this is not your problem this is our problem we gotta go fix this now so so even though yeah i'm not used to these particular tools uh it helped me find an issue in safari um so they're going to say that now uh which is good yes and i feel it is part of their reader code because this this bug doesn't well they were able to do a raw webkit build and confirm you don't have a memory leaking webkit you do in safari because of internal safari things uh yeah and it wasn't even the only browser bug i found as part of this i actually found that resize observers leak in all browsers um always good although it's now fixed in chrome and it's fixed in webkit and i think it's fixed in firefox as well i'll put a link to that in the description if you want to see more about that but yes right now it's advisable to manually disconnect your intersection of servers and resize observers these slides took a long time to do because i kept running into actual browsers to solve but there we go um that is how to to fix some real in the wild memory leaks well yeah i learned something jake so did i so what i'm going to do here is i'm going to take a heap snapshot [Music] a heap snapshot a profile [Music] right let's see if i can go back and do that again [Music] i don't know
Info
Channel: Google Chrome Developers
Views: 19,198
Rating: 4.9818349 out of 5
Keywords: pr_pr: Chrome, type: Interview, GDS: Yes, http 203, debugging memory leaks, how to debug memory leaks, developer, developers, chrome developer, chrome developers, google chrome developer, google chrome developers, chrome, google chrome, chrome dev, web, google
Id: YDU_3WdfkxA
Channel Id: undefined
Length: 22min 4sec (1324 seconds)
Published: Tue Aug 03 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.