 Hey guys, I'm Shashvat. I work at Flipkart. Here are my two colleagues, Ankit and Ravi, who has helped. So we together have investigated a little bit about JavaScript engines, not JavaScript itself. So Flipkart, I'll just go in a very brief, that why we did this and all. We have started using Node.js at Flipkart. And as such, there was a very completely different experience when you use JavaScript in our server side as on browser side. And as with other languages like Java and all, which are VM-based, you experience unknown pauses when there is a lot of memory consumed. And that's the logical cause being the JavaScript memory management and all. So we investigated, we dwelled deep into a couple of JavaScript engines, SpiderMonkey and V8, and tried to find out what is all this mayhem about. So I'll go through a little introduction about what SpiderMonkey and V8 are. I think most of you guys know. A little bit into a theory of garbage collection. Then I'll talk about garbage collection strategy in SpiderMonkey, which IonMonkey is the latest version of SpiderMonkey, and V8. And then there are two closure sessions, which is why should you care? And some tips and tricks you can do to avoid memory pileup or GC pauses. So JavaScript engines is a SpiderMonkey and V8. Mostly sent to the IonMonkey. IonMonkey, they are doing experiments in garbage collection, specifically towards incremental garbage collection, generational garbage collection. So when you talk about JavaScript engine performance, you mostly talk about these three things that when Google launched V8, they talked about fast property access, dynamic machine code generation, and efficient garbage collection. So fast property access is that when you access a property in JavaScript, it's mostly most of the JavaScript engine or old-time JavaScript engine keeps it as a hash map. But Google came with something different. They came with a mechanism of hidden classes by which you can directly access a property just like you access a property in a statically typed language. Dynamic machine code generation is, again, it's more closer to generating just in time machine codes for JavaScript to make it efficient. And the last one is efficient garbage collection. So this is another place where they went away from the leak, which was a leak of Mozilla, another very inefficient JavaScript engines, which were using a stop-the-world garbage collection. So I'm going to concentrate on garbage collection in this presentation, and we'll talk about what the different strategies that these companies at SpiderMonkey and V8 utilize. So yeah, basically these are the points that we went for garbage collection here in this talk. Mozilla working on IronMonkey, one of the main focus areas in V8. Yeah, basically you got the point. So GC is the basic, very basic principle of GC is find data objects that the program has created is no longer required, and reclaim those resources. So the problem with this approach, against a traditional approach where, in a statically typed language, where you are responsible for creating or allocating the memory and releasing the memory, is that GC runs. When you allocate a memory, you don't care about releasing it in a VM, in JavaScript, or in other VM-based languages. So what GC does is, GC has to somehow collect those objects. And in the process, what it has to do is, it has to find out what all objects have expired. So it needs a lot of computing resources to find those objects, which causes unpredictable pauses in your program, if the GC is heuristic based. And then bad implementation of GC virtually lowers your available memory. Because if you haven't collected the objects, those memory is not available for you to reallocate again. So basically, if the garbage collection implementation is bad, you end up with a very less memory available with you. So what SpiderMonkey does is, they use mark and sweep algorithm. Basically, they mark all the reachable objects. Basically, the objects that are currently being used. And they don't mark the not utilized object in the mark phase. And then in the second phase, they call the sweep phase. They go and collect all the unallocated or unmarked objects, and release the memory allocated with these objects back to the pool. So sweep phase will basically unmark the marked objects. What SpiderMonkey in the latest version of Firefox 16 came up with is incremental mark process, where the traditional mark process is a stop the world process, where what happens is that it will stop the execution of the JavaScript execution. It will find all the objects, clean those objects, and then resume. But now what they have done is they have spread out the marking to a larger period of time. So suppose if a traditional garbage collection takes 100 milliseconds, one stop 100 milliseconds, they have divided into 10 stops. And they do garbage collection in a cycle of 10 milliseconds each. So now within those 10 milliseconds, your application can respond. And the performance is better, way more better. So incremental, they have break into small things. But what VA does is something different. So remember that in sweep phase, SpiderMonkey has to go through the entire heap. And it's a very big heap. What VA does is it divides the heap into, based on the age of the object. Basically it divides the heap into two parts, known as young generation and old generation. Young generation is small, where all the objects are allocated. And old generation is the part where the objects that have higher life cycle moves. So what happens is it allocates memory into young generation. When young generation gets full, it does a small GC. Moves all the objects that are still live into old generation and continues on. Now you have young generation free for more allocation. Then it does a full GC. Then it does a full GC to clear entire memory if it finds that both old generation and young generation are full. Now the advantage here is that your small GC pauses are very small now. Because you are iterating over a very small heap. But the full GC is still stopped the world. So keeping this point of view, what we did was we did a comparison of different these two engines based on what is the effect of these two engines on the processing. So we have divided the comparison into three parts. How much computationally intensive these engines are? Second thing is what is the effect of pauses? Third is how and what is the effect of garbage collection on memory? Memory management by your application. So if you talk about computational resources, both the engines come takes around same amount of resource. Because primarily because GC in both V8 and Spider Monkey is single threaded. And what ends up happening is that they stop the application for say 10 milliseconds. They will collect as many objects as it can and moves on. So both engines basically the computational cycle that is they consume is directly proportional to the number of objects the application has created and the size of heap. So computationally both the engines are comparable. There's no much difference. What is the impact on pauses? Now again, with the new Firefox 16 version, the pauses they have reduced the pauses. That doesn't mean so what they have done is they have broken the 100 millisecond cycle into 10 millisecond cycle. So that way they have controlled the pauses. But that have other side effect that we are going to talk about. But still, Spider Monkey has to scan the entire heap. Though it is doing it in a smaller cycle, V8 can do it much faster, much better because it has to scan young generation only. For most of the time, old generation only when young generation is full. But still when V8 executes a full GC, it has to scan the entire heap. So that's why full GCs have higher time. They usually take around 300 millisecond to 400 millisecond. So example being. So this is a small application that Firefox 16 guys developed to show the incremental pauses. I'll run it here. So this is the effect of. So this is what it is saying that how much time it took to basically collect from a heap. And the horizontal axis, basically the width of this graph shows how long it took. So if you see that with new Firefox 16, the collection cycle is not more than 44ms at any event point of time. So this is a test that was purposefully written for Firefox. This test doesn't perform well on Chrome. So if you see the pauses, pauses are going to 86 milliseconds. So I'll talk about why this is happening here. So what's happening is in this test, let me stop it. So what's happening in this test is that they've created a very big array, and they are allocating objects to it. So the array size is around 6 million objects. And they're creating, in each short burst, in each instance, they are creating, generating 8k objects. So what's happening is in mark and sweep, since the sweep time, the mark time is very small, only 10 milliseconds. And they don't care about memory in this test. So this test is running fine on Mozilla with 10 milliseconds pauses and a full GC pauses going around 22 milliseconds. But what happens in Chrome or VA it is that since there's a young generation and old generation, young generation is getting filled because there are 6 million objects. It is pushing all the memory to old generation. And gradually, old generation is filling up as well. And that's why there is a full GC happening at that point of time, 446 milliseconds. So I changed this test a little bit. What I did was I took the original array where 6 million objects array. I made it 1024. That is 1k objects only. And each cycle I was generating only 100 objects per second. Now Chrome was performing much better and has a very nice straight line of 16 milliseconds flat on this experiment, while Firefox had the same experience. So this is the effect of your garbage collections, the type of garbage collection you use. So I'll talk about it in a little bit more detail. So memory management. So what is the impact of JavaScript's strategy in memory management? So what happens is, first of all, if your garbage collection engine is not written properly, basically it is leaving out unused memory like that. It will reduce your available memory. So what happens in that if you start collecting random objects from the memory heap, it starts leaving gaps. And those gaps may not be contiguous. And those gaps could be between two very large chunks of memory. There's a small chunk of memory in between. That's called fragmentation. I think all of you know that. So what SpiderMonkey GC does is it yields less usable memory after each GC. And that's what is perceived as memory leak in Mozilla. But V8, what V8 does is, once in a while, say, from after third garbage collection cycle, it does a compaction of the memory. So basically, it finds out these gaps and moves them into a contiguous space. So that's why if you run this experiment or if you run any experiment and you quit, you stop your experiment, you will find that V8 or Chrome, Chrome is not going to consume more than 300 megabytes, while Mozilla ends up consuming around 800 megabytes. And it never releases it until and unless you close the tab. Another thing that GC strategy affects here is object allocation rate. Both SpiderMonkey and V8 GC will not play nicely if you have a high rate of object creation in a very short while. Because both, in both cases, your memory fills up very fast, and whether it is young generation or whether it is a full heap scan, you are going to take the same amount of time to reclaim the entire memory. So in this context, both SpiderMonkey and V8 are equivalent. Another thing is object lifetime. So remember, I told you about that there is an array. In the experiment, there was an array and 6 million array. And the memories were getting added into that array. And it was getting cleaned. And there was a full GC running in Chrome, but there was no such case in Mozilla. So the fact here is the observation here is that V8 is not very friendly with the objects which have higher lifetime. If you create many such objects, and if you continue to create so many objects that have higher lifetime, then it takes to move from young generation to old generation, V8 will start crapping out. SpiderMonkey, there is no special effect there, because it anyway is doing full heap scan every time, and it is collecting those objects. Another thing that GC strategy impact is time it takes to allocate memory. It's very subjective. Usually V8's memory allocation is faster in most of the cases. But what happens is if your contiguous memory allocation is larger than the young generation memory heap size, then V8 has to move some objects from young generation to old generation, basically do a small GC. Or if the space is not there in young generation, it has to move the entire object allocation to old generation. And it has to do a full GC on old generation to get reclaim memory if it is not sufficient. So there it takes more time to allocate V8 in special cases. SpiderMonkey, again the special case being that if the memory is highly fragmented, SpiderMonkey will suffer. Because it will not find the space, and it will give you out of memory exception. So why should you should care about all these things? So you should care because if you are going to write a game, which is going to have to have 60 frames per second, then you will get only 16 milliseconds to do your job. And in that 16 milliseconds, if it is consumed by your garbage collection, then you will not be able to give you a very good user experience. Again, when you are rendering graphs. So we were doing some experiments in Flipkart around rendering a very large amount of data, some 20,000 line item data which has around 20 columns. So what was happening was during pagination, after one full page view, our entire application was halting, mostly because of there was a garbage collection running at the background. Again, web applications such as spreadsheets and maps where there are a lot of tiles that has to be moved around or that has to be juggled. This causes a problem. Having a GC which will pause the user experience when you are trying to move a map from one place to another or if you are moving from one cell to another. So in this process, basically whatever I've discussed in the last point, what you do is you generate a lot of garbage. There are DOM events flying around. You are adding callbacks and event handlers. There is you are modifying DOM elements. You are adding to the DOM element, removing from the DOM element. You have such, I talked about the data that you are rendering into a graph. Then there are discarded elements in animated or interactive applications like there are map tiles. There are discarded, say, character objects when you are developing a frame. So you generate a lot of garbage there. So that's where, when you generate a garbage, your garbage collector will run and it will cause an effect there. Finally, I'll leave you with some tips and tricks. So the thing is that both the garbage collectors are very friendly with very short time, with very small objects which live for very short time. So try to use variables in a very narrow-scape, as narrow-scape as a scope as possible. And use wire inside the function. I think that's a standard practice in JavaScript, but I have seen a lot of code where it is not practiced. That people leave out wire when you are declaring a variable in the function, and what happens if it's end up declaring in the global scope. Another thing is that use small objects, large objects. Again, these spider monkey does not have a very high effect on large objects. But in v8, small objects, large objects tends to go from one generation to another generation, which cause extra GC cycles. And finally, if you can't avoid large objects, what you should be doing is you should be doing object caching. So yeah, that's what Google Maps does when it juggles styles. It creates local caches and all. Yeah, I think time is up. Any questions? I think I agree that it's a short presentation. It's a lot of stuff that I could have covered, but I didn't. So I'm very happy to take questions or anything, any doubt you have in this time. Can we get the tests or any samples from which you have acquired these results? Yeah, yeah. So this sample, basically I took this sample and I worked. This was released in Firefox. When they released incremental GC in Firefox 16, they have a blog post where they explain this example. I have tweaked this example with different parameters to test it on. So it's, again, it's freely available. I will send you a link or I can share the link. OK, anyways, I'll move on. I'll send you the, we are at the Flipkart booth. You can contact us there. Any other question? It is also will be there, where we can manually run the GC. Of course, it depends on the virtual machine and everything. Like that here, whenever we are free at not using the memory, we can run the GC. No, so in Chrome and Mozilla, there is no JavaScript function that can force a GC. But there is a Mozilla plugin which can do it for you. There's called Memchecker. Memchecker, let me just find it. So if you see there is a, this is the plugin. Memcheiser, sorry. Like, I mean, once again, this is the GC concept. I mean, when you guys are doing this GC stuff, I mean, based on the type of browser, you are writing the code. I mean, like, if it is a large chunks of objects, in Chrome, it sucks. Whereas in Mozilla, it is fine. Means when you are writing the code, depends on the type of browser, you are allocating the memory accordingly or? No, no, no. We don't do that. What we do is we try to follow these guidelines which are friendly with both the browsers. Yeah. So point of this exercise was, first of all, we were trying to render a lot of data on web pages. Basically, something like showing 1 million, so when you are creating an analytic software, you have to deal with a lot of data, and you have to render that data or graphs on that. So what happens is if you move from page to page or data to data in the same web page, this lot of data actually consumes memory. And because of this memory, GC runs, and your performance becomes very sluggish. If you are doing an interactive. Again, another thing is that if you are developing a web server on Node.js, you have to be aware of these things because Node.js is a server, a pause there means that you are blocking tens of thousands of requests. So Spider-Bunk is not useful in that case. We did it as a comparison that what other JavaScript engines are doing. Yeah, I just needed one clarification on that point where you said the V8 is not friendly with objects which are kept in memory for a long time. Yes. So in game programming, you create an object pool and you keep reusing it. So that's. I didn't get the part. Yeah, in game programming, you create an object pool and then you keep reusing it, right? So does that mean are you referring to those kind of objects, objects which are used in games? So I'm talking about that use local object caching only if you are generating a lot of, say, tiles or some same set of data you are working on again and again. Only use them as a pool. Avoid pools most of the time. Create short objects, small objects with short lifetime. OK, I also heard that WebKit is very good with closures. So does that mean we should, does local object caching means you have more closures for V8? No, we don't do any special treatment there. And WebKit, I haven't worked with WebKit so.