About this talk
In this talk, Garrett Rumad discusses the intricacies of garbage collection in Java and its impact on application performance. He explains how memory management works within the Java Virtual Machine (JVM), emphasizing the importance of understanding garbage collectors and their various types. The speaker walks through the mechanisms of memory allocation, including the heap and stack, and how unreachable objects become garbage. He outlines different garbage collection strategies, including mark-sweep, mark-compact, and generational garbage collection, highlighting their advantages and disadvantages. Additionally, he introduces several garbage collectors available in the JVM, such as G1, Shenandoah, and ZGC, each tailored for specific use cases and performance requirements.
Full transcript
Okay. So, last talk for today in this hall and I'm extremely happy that it's not going to be AI. Yes, as you see, there's still Java in this world. uh it's still very useful and uh once again to make it very very useful we're going to talk about trash uh because yes we all care about performance we all care about how good it looks but actually the
most of the performance is getting from where you actually store your trash and how you manage your trash and um once again I'm quite happy that Garrett is make two sessions today. Yes. By the way, remember Garrett was our first speaker of J Prime 2015. Yes. So, once again, >> it's extremely extremely uh great honor and pleasure to have Garrett. So, once again, applause for Garrett. >>
Thank you. Wait with applause when we when we finish then. Hopefully, it's worth it. So yeah, first of all, you made it through the day. Awesome. And now it's garbage collection. Who likes garbage collection? Yeah, a few, right? Not really a lot of people who knows about it at all. So uh my name is Garrett Rumad. I'm working for Azul. We do JVMs. And because we have
more than 120 slides, I just skip this part and we directly start memory management in the JVM. Um, it's automatic, right? So why care? Who sets who in the audience sets a specific garbage collector in production? One, two, and the rest just use the default, right? And it works. Great job. It's cool. Still sometimes it makes sense to understand the basics behind garbage collection because you might
run into trouble and then usually what people do is oh uh give more memory. Ah didn't work. Give more CPU. Uh also not damn it. And then usually it's garbage collection and then you have to dig into it. And if you know some basics it helps you a lot. So first of all what is it? uh memory management in general has an impact on application performance on
the JVM on application responsiveness and also on the system requirements in terms of memory and CPU usage you need and um just as a little reminder how it works in the JVM everything is based on a thread right and every thread comes with a stack and on that stack for every method that will be called in that thread We have a ST frame, right? Every method has
a ST frame and on the ST frame who was in case session before me know already primitives and references will be stored on the ST frame, right? And this might change with project Valhalla somewhere in the future where also value types will be stored on the frames but at the moment not but in the future probably. So if we run out of stack space, stack overflow, you
probably saw that. So then we have the heap and in the heap we have the actual objects stored and this is not threat safe. It's shared memory. So we have to do garbage collection in this space not in the in the stack. And then there is another space which is the meter space. It's class meter data constant pool method bite code and so on. But in this
session we don't look at this one. We just focus on the the heap and the stack. Okay, good. I hope you can read it. It's might be a little bit small. There's a person record. Could be a class, doesn't matter. And we have four instances of this person. And then we have a list that keeps a hold of all these four persons, right? So if I print
out person one, I get my name. Okay, cool. So this is the thread with the stack and the stack frame. It contains the references to the objects right to the four persons and to the list. And then we have the heap where we have the actual objects that consume memory on the heap and the references point from the stack to the actual object on the heap. Right?
This is how it works. So that means we can reach all the objects by following the references from the stack frame. Okay? So all these objects are live. That's when we say live objects. we can reach them. We can find them. So now I set the person P1 to null. That means the reference is gone. You might think, okay, can't reach it anymore. If you take a
closer look, you see, oh wait, the list still has an internal reference to the object. Even if the object already is set to null, it's still on the heap and you still can get it as you can see in the code. system print line persons get zero will give me the my name from the person one because it's still there. If I remove now the reference to
the list by setting list to null, it's gone, right? And then I can only reach these three objects and the other stuff that is lying around. This is garbage, right? It still consumes the memory on the heap, but we can't reach it anymore. So, we can't use it. So how to get rid of that stuff? Garbage collection. This is the main idea behind it, right? This is
how it works. And garbage collection in principle, it's a form of automatic memory management. There's also something like reference counting. If someone does Swift, they have automatic reference counting. It's just a different approach. Both have their pros and cons. And there's nothing better or worse. It's just a different approach. And it's made to identify and recra no longer used memory. So the stuff that was purple that
was lying around, we have to somehow get rid of that. And it ensures efficient memory utilization. This is the biggest point. It should free you from managing the memory on your own. So if you ever did C melo free, all these things you don't have to do in Java, right? That was the main idea behind it. So we only talk about precise collectors. Um there are also
other ones but in the JVM we only have precise ones and they handle this whole process in phases. So the first phase is tracing. So this should be you will see a lot of these matrices here on the right side. The gray cells that should be the the memory cells on the heap and everything I show you is super simplified. It's way more complex, but you should
get the idea, right? The white squares, this is the ST frames, right? Where we have the references, they point to the objects on the heap. And the blue cells, this is used memory and it's live, right? So, it's a referenced cell. It's live. So, now we have these references pointing to the objects. Now, if we would identify the live objects on the heap, we just follow the
references. Then we mark them. Okay, I found it. And then we mark him here with a red spot in the upper left corner. Might be a little bit small. And then we say, "Okay, it's marked. It's live." And then we go to the next one to the next one until we marked all the the live memory cells. Now we know, okay, these cells are live. We can
reach it. Fine. Next step. If we dead objects on the heap like the purple ones, how do we find them? We have to clean them up from the heap because they consume memory. But it's hard to find, right? Because we don't have references pointing to them. In this scenario, the only way to do it is going step by step to every memory cell and check, is it
live? Is it not life? And is it used but not but not live anymore. So like this one, we have to free it. Then we go through the whole heap and free all the purple cells until that's done. And then it's free. So freeing is not for free in this case. It takes some time if you use this approach. Okay. And then there is another step which
is uh and then after we did that we have to unmark. So we just reset the bits this this little square in the header of the objects. We just reset it and then it's gone. And then the the third phase is compaction. Uh if people are old enough, let me look around. I see some great people. So they remember Windows 311. There was something like disk defragmentation
which looks pretty much like this. And it's the same idea. Somehow we have to compact the heap means moving all the objects to one corner. Let's say in this case I would like to move the the one with the white arrow on to the second from top position. So I just copy it the reference still pointing to the wrong position. Right? So we have to update the
reference too. So it means next step update the reference. We copy in the next cell or move it update the reference. Move the next one. Update the reference. Done. Okay. As you can see, if we compact the heap, it has a few advantages because if we now allocate memory, there's nothing in front of us. We can always just allocate because it's everything is free in front of
us. This is fast. This leads to more application performance, right? If you can get higher allocation rates, the whole application will behave faster. Okay, these are the three phases. And then there's something like stop the world. Who knows what stop the world is? Ever heard of it? Okay, because let's say 10 years ago this was quite a topic still. Today not that much but at least you
have to know what it is. So if we have application threads in the JVM and they are running then at some point we need to do garbage collection then the JVM will fire a safe point signal to all these threads and they will go into some something called JVM safe point. Every thread is now stopped and in a safe position. That means we can go from there
in the future. But at the moment the complete application stops. It looks like it hangs. Then the JVM starts and is doing the garbage collection, the marking, freeing, compaction. And once that is done, we just continue running. This orange area in the middle can take up to minutes depending on your configuration and heap size and all that. And you can imagine that's a really bad thing. If
you have something like a web shop and there is black Friday, it's not really a good idea if you're you just would like to buy it and then suddenly it stops for 30 seconds, right? Because garbage collection. So this is the reason why we have lots of garbage collectors for all kinds of use cases to make this orange gap as small as possible. That's the idea. And
if someone talks about stop the world, this is what they mean. And you can run into these problems even today. So, and then we have to talk about different principles of collectors and this is not the collectors in the JVM. This is really just the basic principles and I try to make it as entertaining as possible because it's quite dry stuff. So, we have the non-moving collector
mark and sweep. Probably most of you heard about that. This is the heap, right? And what it does now we will allocate memory once it we run out of memory then we will stop the world. We will mark all the life objects and we'll remove the dead ones. Let's take a look at that. So we allocate memory and you see objects die. They get purple. We remove
them. They get to null or whatever. So at one point the heap is full. Then we have to stop the world, mark the live ones, remove the rest. So now stop the word, mark, remove, continue, unmark and then continue. You see already ah that's a little bit tricky now because the heap gets cluttered right objects sitting somewhere and this is in this case easy peasy because every
object is one cell in reality objects span more than one cell. So that means if you have an object in this case which needs 15 memory cells in one row super hard to find right? So you have to check oh there it fits okay it gets harder and harder and at one point if it doesn't fit anywhere out of memory exception even if there is free memory
but this is the problem. So this is a non-moving collector because we don't move stuff around. We just if if it's if the object is dead we just remove it. We have to go through each cell check it okay it's dead remove it and then once we're ready unmark continue. So fragmentation huge problem. Remember Windows 311 disk defragmentation was exactly the same thing. So that leads me
to the next ones because we have two moving collectors, right? There is the compacting collector and there's a copy collector. And so let's first take a look at the mark and compact. It's exactly the same. I just already started. Now I will stop before the heap is completely taken and then I will mark the objects remove the that ones and then I will compact. You will see
it right now. Stop mark remove unmark compact. Continue. Big advantage. We can just allocate memory now because it's always free in front of us. Okay, this is mark and compact. But because we move things, we have to update references. It's an additional step. Takes a little bit longer. Okay, but and the other thing is we need some spare space at the end because we copy stuff around
that need means maybe we have to first arrange some stuff to make some free space to copy it. So we need some spare space and this spare this headroom it could be up to 50%. It really depends on the setup, but it takes more memory that you actually can use. Okay, so the heap is bigger than you really can use it. And this is this is actually
um this this is used in in the JVM this collector. Okay. And then that's my favorite one uh copy collector. Once you see how it works, it's so cool. This different now. Now we have two times the same space has different colors. uh in the JVM we say often it's a survivor space one survivor space two you can also say to and from space right so what
we do is now we allocate memory in the orange area it's this the two space once this area is filled up we stop the world we mark everything that is live and now that's the trick because the gray area is free we just copy everything that's live to the gray area and because it's free it's directly compacted and then we No, because everything that was live, we
moved to the gray area. We can just directly wipe out the orange one. We don't have to go through each step, right? It's way faster freeing and we always have a compacted heap. The drawback is we need twice the amount of memory, right? Let's take a look. So, we allocate memory. Objects die fast. That's how it is. Not only in Java, it's everywhere. And it's because if
you have an object in a method, as soon as you return from the method, everything that was in a method, garbage. So stop, mark, copy, remove the rest, and then toggle the spaces. Two becomes from, from becomes two. And then we do the same thing in the other direction. So super simple approach, very effective. Like I said, the drawback is you need twice the amount of memory,
right? The other drawback is if you have objects that live for the whole application lifetime, you copy them over and over again. Even if you know they won't die, but you copy them, right? That's the drawback. So each of these bas basic principles that I showed you alone really don't make sense. But if you combine them, it they become quite powerful. Let's take a look at that
one. So long living objects and twice as much memory. That's the drawback. Apart from that pretty cool collector. So leads me to this one generational collector that's a combination and trust me this is the last one I will back you with afterwards then we go into the real So the generational garbage collector this is um there is a so-called weak generational hypothesis that means objects die young.
So we have the number of objects on the y- axis and the lifetime of the objects is on the x- axis. So it looks like this. We have super many objects that die super fast. We allocate use die. Then we have a couple of objects that live a little bit longer before they die. And then we just have a few objects that live really for a long
time. Okay. So now if we use these principles I showed you we can shape a garbage collector according to these principles. We take a space where we allocate memory and this space doesn't have to be very large because objects die young. Okay. So because it's not very big garbage collection super quick in this area. Okay. So then we use a copy collector for the medium living objects.
That means if an object survived the first cycle, we just copy it to the copy collector and in the copy collector now we can set a threshold let's say 15 times. If it survives 15 times then we move it to the old generation or to space the blue area right and there it can sit for forever. Now, you might wonder, okay, that that looks cool, but why
is the blue space so big if we just have a few objects in there? Doesn't make sense at the first glance. If you're lucky, you will never fill it up. So, that means you never have to do garbage collection in the blue area. That's the reason because we for a long time. So, we really have time before we can or we before we have to do a
garbage collection there. And I already mentioned it because we have two different generations. We have the young generation and we have the old generation. And we have two different garbage collection cycles going here. So we have the minor collection. It's only for the young generation and we have the major collection. It's only for the old generation. The worst thing is the full collection if we have to
do both. Right? So this is how a generational collector works in principle. There are other approaches like IBM has a different JVM implementation. They for example have a garbage collector that they don't have the Eden space. They directly allocate in the copy collector. Apart from that it's the same thing. So the principles are always the same. It's mark and sweep mark and compact and copy. With this
you can do most of the stuff that is needed. Okay. Again I have a little demo. Let's take a look at this and then you will see the benefit. So we fill up, we allocate, stop the world, mark the live ones, move it to the copy collector, remove it from the Eden space and continue collecting. Then again mark, copy and so on. I just do that once
we promote some objects to the old generation. Then I will do fast forward because it really takes long even with this small thing. So they have to survive three times in my example. So now it goes fast forward. Now you see how it works and you see there's even in the old area objects die slowly. So we have some kind of a wavefront moving through this space.
And if this fills up we have to do and now do a full collection a little bit slower again because now we are running out of space in the uh old generation now doesn't fit. Stop the world mark compact. Then we copy everything that's live into the old generation and start from scratch. That's a full collection. And then we go again. This is how this generational collector
works. So it's quite interesting. You see the benefit of having this copy collector, right? Because at some point it says, okay, now it survived three times. Just move it to the old one and then we can allocate fast again. Allocation rate is king. So if you can allocate fast, the application is fast. That's the reason why this principle makes sense. We would like to have as fast
as possible memory allocation. Okay. And there's an actual or was an actual collector that really looks pretty similar to this and we will come to that and it has also drawbacks. All right. So now we take a look at the collectors in the JVM. Um we start with the serial collector which is the oldest one of all and the most simplest one. So I I have a
some kind of a spec sheet for all of the collectors. So you see availability, if it's parallel, if it's concurrent, generational and so on and so on. Um interesting might be heap size and um throughput and CPU overhead because that's different with these collectors. So this is usually quite useful collector of if you have very small microservices that are not very say I I'm not really I
don't really care about pause times for garbage collection then this might be a good collector even today even if it's 30 years old or 31 already. So um how it works is in the young generation you see that orange stuff is the stop the world pause and it's a serial collector. We just have one thread that does the garbage collection in the young generation and also in
the old generation. That's the reason why if you have a very bad setup, this can take seconds, even minutes to do a garbage collection, right? Because it you just have one thread who does all the jobs, which is quite annoying. There's another thing that you don't know and we saw how many people set a specific garbage collection in in their application. If you have a micros service
and you set it to two gigs of heap and you have one CPU or let's say you have two CPU even better you have two CPUs and one gig uh two gigs of heap all good you use G1 now management makes a decision we have to save money cloud is expensive can we reduce the heap ah sure let's move it to 1.5 GB bam suddenly and you
don't specify the garbage collection the JVM will decide for you and it says oh you you have less than 1.8 GB of RAM. This is not a server machine. I give you serial collector, right? This will happen also when you decide, oh, I just need one CPU. Bam. Serial collector. So be aware. And then suddenly you might see garbage collection processes that you never saw before because
G1 is way faster than serial. So this is something where you have to be careful. This will go away in JDK27 because then G1 will take over all this. So there's no default ser fallback to serial anymore. But the reason for that is that at the time they implemented it, they couldn't imagine that the machine with one CPU or with less than 1. Gigs of RAM, it
couldn't be a server, must be a desktop machine. That's why they decided to give it serial collector, right? So you you can run into this problem. So because they knew that this works but it's not very performant they also developed parallel GC which still is the fastest garbage collector you can get right when it comes to throughput throughput is defined by time you spend in your application
versus time you spend in garbage collection okay so this one is fully parallel not concurrent it's a different thing Right. And this is good if you have a multi-core system with a smaller heap like 4 gigs or something. And it's good for batch processing, scientific computing and stuff like that because you still have garbage collection process. But this is how it looks. The gap is way smaller
because we use multiple threats in parallel that do the garbage collection. Right? we still stop the world but if you have 100 threats available probably most of them will be used for garbage collection then and that's pretty fast so the throughput is super high with this collector even today this is a very good collector it was the default between JDK 5 and JDK 8 then this was
the default collector from JDK9 onwards it was G1 so okay parallel GC throughput collector um then this one concurrent mark and sweep deep. That was the first garbage collector that was not only parallel but it was also concurrent. Partly concurrent means it does garbage collection at the same time while the application is running. Right? You can imagine that's quite tricky because you move stuff on the heap
around while the application still accessing it. That is tricky. You have to do locks and all these things. It's in the meantime deprecated. Uh it was removed in JDK 14. So if you're in 17 and above, you can't even switch it on with this switch on the lower right corner. It's not possible anymore. Like I said, it's partially or was partially concurrent. The idea was because serial
and parallel haven't been predictable. So the pauses could be longer or not. You never know. With this one, they could at least guarantee it's shorter than 1 second. That's okay. That was okay at that time, but they decided to not continue because it has two problems. First of all, the code was super complex and the developers said, "No, we don't want to support it anymore." The second
one was that in the old generation, remember the blue area, they just did mark and sweep, no compaction. And then you can imagine if you have a huge area that is the old generation and you fill it up and you do garbage collecting and you just pick stuff out and it gets cluttered all over the place and that really leads to problems over time. So that wasn't
really the best thing to do and this is how it looked like. So you see some things have been already concurrent like concurrent sweeping. There has been concurrent marking of objects. This and then there's still stop the world forces in there. Okay. And now to this one. That's the default at the moment. G1. And this is different from the others. Remember the other collectors have these big
areas like Eden, Survivor One, Survivor 2, and Tert. This now looks like this. It's a region region based collector. So we have all these little regions. That's the bigger squares. And these regions, the size of the regions is defined by the heap size that you have. You see the table on the on the left, right? So it can be max 32 megabytes, one of these regions. The
smallest one is one megabyte. So you can imagine doing a garbage collection in this small area super fast, right? even 32 megabytes not really that big compared to these old big chunks like the heap could be I don't know gigabytes right if you use it in the other other scheme so now we still have Eden regions we still have survivor regions we have toert regions and there's
also humongous region right um how it works is the regions are not bound to the type so they can be flexible right each region can have every type and um you define the number of how many Eden regions you like, how many uh survivor regions and so on. And the humongous region is a little bit specific. It's if an object is bigger than half the region size,
the object will get the whole region. And that leads to a problem as you see in the lower right corner. Hard to read. If it doesn't fill it up completely, the rest of the region is unused space and you can't use it. You might think, well, I don't care. Well, there are customers of us. They ran in problems where they ran out of heap even if they
thought we don't really allocate that much memory. The problem was that the region size was nonoptimal for the object size. They had lots of big objects. So that means they really took all of these humongous regions and the humongous regions are like old generations. They won't be touched anymore, right? They get the space once and stay there forever. And because it was slightly above half the region
size, they literally lost half the memory on that humongous regions. So this can happen, right? So that that's the reason why you have to be careful with setting the heap size correctly for G1. But it has lots of parameters to set. So let's take a look how that works. In my example, I have six Eden regions, three survivors, and then we fill first we fill the six
Eden regions. Once we fill them up, we it's a super short stop and we copy from the regions with the most garbage, we take all the life objects and copy them into the survival regions. And then we continue and objects die and it continues as we see it saw before. always six regions Eden the two with the most garbage I just take the life objects and copy
them into the survivor regions and so on so and then they finally end up in the old generation and if if they die there then at some point we just remove the complete old generation if it's if it's empty so this is how it works you can already see this is way more flexible than these big chunks that we saw before and this is what all the
newer garbage collectors use these days. They all use they're all region based because this is way more flexible and also faster because we have this smaller regions. So garbage collection doesn't take that much time. Okay, that goes on forever. Let's skip. Um it's around 100 to 200 milliseconds usually the garbage collection pause. It can go down to 50, but usually it stays around 100. I think 200
is the max time it needs and that's why it's a really good collector as a default. It has a sweet spot around uh 16 gigs. That's quite there's the best performance. It works with smaller heaps. It works with bigger heaps, but 16 is good. 16 to 32 works quite well and it's um a good collector for most of the use cases. And as you saw, most of
you use it and you're happy. So, it works. Um you can see that the CPU overhead it increases a little bit already because this is already partially concurrent. Keep in mind if you do garbage collection concurrently means you run your application at the same time you do garbage collection garbage collection also needs CPU and memory right so you get memory and CPU from your application to do
garbage collection right and that get gets worse if you do a full concurrent collector as we will see they use even more of the CPU so this is how it works we don't go through the diagrams because they will get more and more complex Now this is the default collector since JDK9. It you could already choose it in JDK 8 but 8 but you have to switch
it on using the switch. Um then there's something called epsilon. Did someone ever heard about that one? Because this is the no collector collector. This is like doing melo in C without doing free. You allocate memory until the heap is full and then you crash. This is and that's on purpose. and it was made and you as you and you take a look at the lower right
corner uh the flags you need to to use it says unlock experimental VM options so they assume that you really know what you are doing right because it will crash if you run out of memory then you might ask yourself okay what's the point having a garbage collector that doesn't do garbage collector cool well it is pretty cool because if you know exactly how much memory Mor
service will use over the lifetime. You can use this one and you don't do any garbage collection. That's more or less the fastest you can get, right? But be aware, it's it's super tricky. You can do yourself a favor and just switch it on, press enter, and I I bet as soon as you leave the finger from the key, it already crashed. I tried that. It's super
You will be amazed how fast the memory will fill up in the JVM. So, but if you know what you do, this is the more or less fastest stuff you can do. The other reason why to choose that is if you would like to test, for example, you work on a let's say a scientific application, you have a very special algorithm that you would like to performance
test. What you usually do is you run that a thousand times and then you average because garbage collection will kick in in between. If you use this one and you just separate the algorithm, you can run just the algorithm and measure the pure performance of that without having garbage collection stepping in. This is the main idea behind it. So it was made for testing. Some companies use
it for super high-speed trading stuff, but that is very tricky, but it is possible. So you can use it also, but be aware it will probably crash. Next one, Shenondor. Uh this was made by Redhead. um and it came with JDK11 as the uh non-generational version and then with JDK 24 we got the generational version which is now uh the stuff that is implemented the non-generational is
not available anymore so this is a different collector this is fully concurrent that means it runs 100% all the time at the same time your application is running the pause times they guarantee are around 15 milliseconds. So that's already super fast. This is made for large heaps. It's also optimized for running in containers. So it's if you run a lot of containers, that's a quite good collector.
Um if you need predictable response times, that's a good collector. It is not an Oracle JDK. It is an Oracle Open JDK. Okay? Because it was not invented by Oracle. They decided to not really put it in their JDK. their decision but in open JDK it is that means all the other distribution have it it's made for using very large heaps so now let me ask who
is using a heap more than 64 GB one because that's pretty rare two three a few people this might be something for you if you have a beefy machine be aware that these collectors if they are fully concurrent they need CPUs they they really shine if they have more CPUs and a lot of memory then they are really fast. If they don't have it might even be
slower than G1. So you really have to it's a trade-off. You have to think about it before you choose it but it might make sense. And you see the overhead now is 10 to 20%. That means if you have a 100 CPUs your application might only get 80 of them for the for your usage and 20 CPUs are used for garbage collection. Right? You have to keep
that in mind. So even if Netflix is using ZGC, you don't have to, right? It's not like with the microservices. Now everybody does microservices because of Netflix. But if you know how Netflix use microservices and they are customer of ours, a microservices managed by around 23 people. That's really micro, right? This is some people call that a monolith, but this is how they work and they have
huge machines. So like I said it's not available in or Oracle JDK you have a reduced throughput due to the concurrency and it makes use of a new barrier concept. Um yeah and I already mentioned it's fully generational since JDK 24. So ZGC the latest and greatest. Um because this collector is a copy of another one. Um I just will go into detail for the other one
and don't really spend a lot of time on this one. Um it's a regional collector similar to G1 but we only have old and young generations. So there is no survivor spaces and all these things also no humongous regions and stuff and the region size is mainly based on the memory mapping of the machine. So it really depends on which machine you run on. So if you
for example run on Linux then trans transparent huge pages can have a benefit here. So that depends. This is again fully generational in the meantime since 21. The non-generational version is not even there anymore. Pause times are around 1 milliseconds can be lower. Um it's made for very large heaps. So it can be in the terabytes if you like and still keep the one millisecond pause. That's
because of how it works. Um if you need if you have very low latency uh sensitive applications or large scale systems then this is really good. Like I said this collectors shine if you have 40 cores. If you have 80 cores even better. If you have 128 gigs of RAM, great. 256, better, right? This is made for huge systems. It's not really for microservices. It will work,
but it gets the most performance out of bigger And because it's a copy of this one, and that was sorry, shameless plug, that was invented by Azul, but it's already more than 15 years ago. So um and Oracle just reimplemented it more or less with ZGC because this is not open source but it's exactly the same algorithm behind it. So that's why it has the same specs
right and I can just tell you that one of our customers with the biggest heap has a heap of 21 terabyte and the garbage collection pulses are below one millisecond in 21 terabyte heap. When I heard that the first time, I really had to think about it. So terabyte. Okay, interesting. My hard drive is smaller than that. So, and that was the heap. So, it means the
machine has even more. Um, as mentioned, bigger machines really benefit if you have a lot of memory. Um, that's part of a zing, which is JVM. It uses, we name it the loaded value barrier. um in ZGC it has a different name and they also talk about colored pointers and I will come to that because it's a quite interesting concept [clears throat] and as ZGC this can
benefit from uh THP some from transparent huge pages. So let me give you an idea what's the difference and why it can be so fast. Um the loaded value barrier and the colored pointers work as follows. This is how it usually works in the standard collector. So you have a stack frame. It points to the heap to the object. Okay. If we now mark it as live,
fine. If we now stop the world and move it to a different place like we do a compaction or something and then we store the new information. Where's the new object now? We store it in the old object header. This is how it works usually. So then if we update the reference, we take it from the old object header and then we update the reference and then
we can remove the old object. This is the procedure. Okay. So if we take a look at C4 or ZGC, we have this barrier which is in front of the of the object and then we have bits on the pointer, not on the object header anymore. So what we do now, we just move the object. we just don't care, right? We don't stop the world. We move
the object to a new location and we just update the pointer and in the pointer we say, "Oh, um, there's something moved and the new location we put in an off heap space or ZGC does the same, right? So, we just put it there, not in the header of the old object. We can just remove the old object. So, now we update the references. This is done
by GC threads. they just take it from the offy page and update all the references that are on the offy page right so it's multiple threads doing that all the time besides the application is running so the worst case scenario is we do this right we move the objects we just cleared the the memory so the old object is not there anymore now suddenly the application says
oh I would like to have that object it follows the reference from the ST frame and then it it figures out we do we name it test and jump right it's a one CPU cycle it just try to reach it and see oh it's not available anymore because we have the flag in the colored pointer so what it does now it fails and then it looks up
the address the application does it right the application thread looks up the new address and then initiates updating the reference this only happens when the the um garbage collection threads didn't pick it up yet, right? If there are lots of stuff in the off page, then this situation might happen. This is a so-called self-healing process. We don't have to stop the world because even the application thread
can update the reference and once it's updated, it's also updated for others. Right? So, this is the main idea and that makes it so fast because we don't have to stop the world and then it can just follow the reference once it's updated. This is the procedure. We don't look into that too complicated. Uh one advantage that we have in our JVM is we have our own
compiler which is faster than C2 because if you run in par in concurrent as I said then we need we have a drop in performance right so Netflix for example announced that they saw 3% drop in performance when they moved everything to ZGC and because we produce faster code we can keep up with this 3% we can even be faster than uh C2 So this is the
big question which one to choose. Now we saw all these collectors. I showed you the principles. Um we have a few essential criterias. We have throughput right we have latency and then we have resource usage. These are the three criterias that we have. Now let's try to choose well we make it easier for you. You can just choose two out of three. You can't get very high
throughput with low resource usage and very low latency. Not possible. You can get low latency and high throughput but sorry resource usage will be higher. They say I would like to have low resource usage and very high throughput sorry latency will be higher. Right? So there is no way to get all of these. Even this doesn't really help you yet. So this might be more helpful if
you don't care about runtime overhead. Right? It's a lower arrow. You don't care. You just say I don't care. I have resources. No But you aim for very short response then you're on the left side of the diagram right if you say ah resources I don't have but stop the world process I don't really care then you're on the right side right this is serial of parallel
all the rest is in the middle and as you saw most of you use G1 which sits in the middle it works quite well the problems only arise on the left or right side of this diagram Okay, this is probably most of the times So, as an overview, um there's everything on one slide. Might not help you yet, but if you would like to have the slides,
you can ping me on LinkedIn because I always change them before the presentation. Um I can send it to you. Um and with this, I'm done. And we still have five minutes for questions if you have some. Any questions? And if you are too shy to ask, then you can also catch me after this. Okay? And just as a reminder, if you would like to take or
be part of the raffle, stay here. Stay here and win. Otherwise, if you go outside, that's it. Thanks for attending.
More from this event
See all 29 talks →
Agents With Seatbelts: Practical Ways to Keep AI Code Gen Under Control, Jonathan Vila López
41:46
Practical MCP Security in Action, Willem Jan Glerum
43:59
Kotlin for Normal Brains (Without Jets), Nayden Gochev
59:30
Beyond the LLM API - What Developers Actually Need to Know About ML, Milen Dyankov
54:58