 All right, hi everybody good stuff. So welcome back. Hopefully you got some pizza and I don't know what else did they have over there? Did you get pizza? I didn't think about it. I just got coffee. I'm the jet lags Intense, okay, so who was here when I was I was just here like a like a month and a half ago or two who saw me last time Okay, good. So we're not gonna do all that again, and there's just been a few things That have sort of dropped in the meantime, you know, so I figured we'll do it very short We don't have a lot of time and and you know, there's only a few things in a way So I figured we'll just talk about those new things. Okay, and you know, these are all things that I think will serve us well but basically my core thesis the my the thing I've just been so excited about is I Truly believe there's never been a better time to be a Java developer Java is now Paradigmatically more important than ever and spring boot is more useful and more profoundly powerful than ever and that's just more manifest in these new versions of Java and the new versions of spring boot and so obviously You know, you saw a Java 19. Sorry Java 21 just came out on the 19th, right? And and spring boot 3.2 is coming out later this year So it's an amazing time a lot of amazing opportunities So we're gonna go ahead and just see all these things in action I'm gonna go to my second favorite place here on the internet. Obviously. Can you want to turn off the lights? Can you see the screen? Is it too bright? I Can do this but that's that feels wrong There you go. There you go. Good stuff. So there you go. There you go So, um, so we're gonna build a new application friends I'm gonna start I'm gonna go to start dot spring that I owe build a new application I'm gonna use maven because right now Guido is a little weird with Java 21 It'll get fixed when I think they're blocked by Kotlin supporting Java 21 And so in theory, maybe that's like fixed by the end of October in the meantime maven it is no big deal We're gonna build a new service. Obviously, we're gonna build a new application. I'm gonna call this service I'm gonna call the service service and the reason I'm gonna call it services because I am great with names Really good with names. I get that from my father. I told you about this, right? My my father was great with names when I was a boy We had a small white dog and my father named him white dog now now this this little dog He he was with us for like 10 years and then he disappeared. I don't know what happened to him I think he got a job anyway like eventually another small white dog appeared at our door and my father named him to And I'm not sure if it's TOO or TWO, but either way my mom she tells me all the time You're very lucky that I named you and I think that's probably it's probably true Okay, so we're gonna use Java 21 my friends now you might notice that here We have four different versions of Java that we could select Java's 21 17 11 and 8 friends You have to use Java 17 or later with spring framework 6 and spring boot 3 or later Okay, so these other radio boxes these are just for the memes just for the just for the joke They're just to find out who makes terrible life decisions. These should never ever be selected ever in any context Okay, these are completely inappropriate in every single situation. So You know, I hope you don't need to be persuaded that Java 17 and later are amazing boss sauce pieces of software And they are much much better. They're faster more performant more syntax rich more robust more secure than Java 8 in every Single way they're technically superior in every single way They're also morally superior in every single way You won't like the look of sadness and shame and disgust in your children's eyes when they find out you're using Java 8 in production Don't do it be the change you want to see in the world do the right thing and friends If you're not already persuaded then I think it's inarguable that Java 21 is almost if you just judge by the By the version number alone Java 21 is almost three times better than Java 8 Okay, so I'm gonna I'm gonna leave it at Java 21. We have some we're gonna add some dependencies here Not a lot just a few. Okay. We're gonna add the test container support. We're gonna add post quiz We're gonna add the Gravium native image support. We're gonna add the dev tool support We're gonna add the web support. Maybe yeah, sure why not and and that's about it. I think post quiz I've got JDBC. Do I No, I don't have JDBC in there bring that in there. Okay. Good. Okay, so we're gonna build a new application I'm gonna wait and hit generate and we're gonna open this up in our IDE up. I did the wrong thing I chose three that one for we're gonna use the milestones for The new version of spring boot again do out in November. Okay. Here we are Okay, very good by the way your Wi-Fi here fantastic great stuff I downloaded four gigabytes while the good doctor was talking. I downloaded Xcode basically it was amazing like well done well done, okay, so Brand new application. I want to just talk about a few things here friends This is a brand new application I'm gonna build an application and I'm gonna have a domain model right and normally, you know when I talk about To people about this kind of stuff. I use records. I love records right records are a great way to model sort of ephemeral state data in a program, right and I just think they're just a much better way to think About building and modeling software, but now with Java 21 and by the way, can you is that should I is that theme? Okay, or like I can change the theme. Okay, so maybe maybe it's too dark. We could we could do like Light mode like that is that better right or my new favorite mode. Of course is the Barbie mode So I'm I who saw that movie. It's amazing. Go see it if you have not so good. So who Barbie Okay Light light mode Who prefers light mode? Boo, right? Okay Barbie. It is let's go. So here we go So we've got this we've got this new type right. I've got this type here called customer And I'm using a record records are great and they've been there since Java 17 But they enjoy a synergy in Java 21 and I want to talk about some of those amazing features is a Java 21 supports What's called data oriented programming? This is what Java language architect Brian gets has been calling it, right? He wrote a great article in info queue last year talking about data oriented programming And and there's just really it's a confluence of a bunch of features that have been slowly added to Java that now Culminate in this new release. So let's say I've got this type here I've got a customer, but you know, let's just let's take a step back here for a second Okay, let's just take a simple step back and and just kind of look at Some of the unique opportunities here are some of the ways that these things play together. First of all, there's records You saw records, right? I've got a record here like this if I go here. I've got a record integer ID string name and when I use that class What if I use that type I have an object that has a a two string equals hash code, etc It's an object whose identity is tied to the components of the object Okay, this is like what you would have gotten if you had used at data with Lombok before, right? And the reason you would you want something like this the reason a convenience like this is useful is because Increasingly our code bases are getting smaller and we're dealing not with API bounds, but but more data Right the data is the way we think about interacting with other parts of the system It's not the methods in the the shape of the API itself Brian gets makes the great point that Historically Java has been amazing absolutely amazing for large code bases because it has great sick great protections for security boundaries for API boundaries for object boundaries and so on it's great at Coordining off parts of your domain model in your object graph and making it so that you have very clear idea of what the shape of those Things are and that makes it great in the monolith right the large code bases with lots of moving parts Java has thrived there No wonder it was So successful in that context, but increasingly we're dealing with small code bases And we're dealing with lots of ad hoc data data, you know coming in from the network from Kafka from HTTP Etc and we're not dealing with large code bases with lots of moving parts The the way we interact with other parts of the code is in terms of objects that we send back and forth So having that be front and center to be able to support data oriented programming is very important So this is the first part of that okay the ability to have a record This gives you the ability to have a nice clean understanding of what the type of what the object is and what the component parts of that is The next feature that works really well in this new context is the new switch expression How many of you have tried this the switch expression the smart switch whatever you want to call it right? We have the switch statement before and I didn't really use the switch statement because frankly it was just too much of a pain in the Posterior yeah, yeah like it's just too complicated. You have too many It's too fraught too many ways to hurt yourself right you have the if you miss a break statement You go through the drop through you don't know what the state's gonna be it doesn't really buy you anything And now you're you know if you're using object oriented design There's also other patterns that would seem to be a little bit cleaner like the like the strategy pattern or the visitor pattern a Lot of ways to like visit a lot of different Implementations across a particular object and see which one matches right so but now we have the switch expression So let's say I I have an enum. Okay. Let's say I have a variable okay var day of week equals day of week dot Friday, okay, so I want to see I want to and then I want to like Print out a message in response to that variable coming into my my method I don't know what the value is though. So I'm going to use a switch. I'll say day of week Okay, I'm assigning a value the switch actually can return a value now It's an it's an expression and you can see the compiler is already helping me out It's saying hey it doesn't have any case clauses fair enough. So I say case Friday Okay, or wait Monday here we go Monday right and What do I want to do if it's Monday? Well, I can say you know, it's I don't know Monday right fine What about Tuesday? All right, Tuesday Etc. So I can just keep doing this and obviously I can also put them together. So Tuesday Wednesday Happy days come on Am I the only one who watched that show? So okay, you can you can merge these branches together but you know Either way, let's see if I let's say if I just tried to leave it at that okay Thursday, okay What happens the compiler says no, this hasn't caught everything yet You don't get this with the switch statement the compiler is telling me you haven't covered all the possible input values So I've got exhaust. I've got exhaustive checking right exhaustive Exhaustion there you go. I've got exhaustion checking It's it's making sure that I've exhausted all possible paths and this is a compiler feature looking out for me You didn't get this with the switch statement, right? It would just let you happily handle three cases and then ignore the others right if you want to if you were if you remembered You'd have a default fall through case But it's up to you you can have poorly written code and it didn't care now the compiler's on your side It wants you to get this right and it knows that since it's an enum There's only a finite set of range of values in that object that you can handle So therefore it'll help you to remember to do that. So okay Monday Okay, okay, sure. Let's just go through the motions here Tuesday And I know I could just do that name, but let's just imagine I'm doing something useful here Wednesday, right? Wednesday Thursday we got Thursday. We went Friday okay Friday and Whatever Friday and then Saturday Okay, and I could do a default by the way. I could do this like Sunday, right? I don't know whatever I could do that or I could just cap, you know handle the Sunday And I'd just do it like that as well. So Sunday Very good. So now I've got I've Handled all the cases and the compiler has helped me out It knows that I've handled all the cases if I get rid of that it complains I haven't handled all the cases. So now I've got a switch expression very clean I get the result I can then print it out, right? It's Whatever, right? There's the day of week. So day of week Okay, that kind of thing now. Oh, yeah, no. Hey, what happened? Oh, it's a Message for day of week. Okay, very good. So the switch expression is really really powerful now Where else can I use the switch expression, right? I've it really benefits us if we have a Fnite set of states. Well, another new feature that was introduced in Java is sealed types Okay, now sealed types in Java 17 didn't really buy you a lot But it really comes into its own when you use it in conjunction with this switch statement Okay, so a seal type is let's say I have the you know, you know, you can imagine this very boring hierarchy, right? interface animal, right? Okay, very good And I want to have a cat and it's going to implement animal And I've got another one here dog implements animal and what do cats do they meow? Right, okay, so let's see string meow return meow, and then what do dogs do well dogs Bark yeah, exactly wolf. So there you go We got I want to like have a way to handle Getting the message from these different cats or different animals right these and I could use I could just have a generic forced sort of Method here called talk or communicate or something, but I don't really need to I just have only a few types And they all have their own particular Structure so it's okay to respect that but I want the compiler to help me iterate over these possible values So I'm going to say var, you know var message I'll do message equals switch, and I've got an object okay var Felix equals new cat, okay, and I'm going to pass this in so you know, let's just say it's a it's an object here Okay, I'm going to imagine this is a method and I'm just passing in an object the compiler doesn't know Okay, so I've got now this object Or sorry, it's an animal there you go animal, okay, I've got this object of type I've got this object. I'm passing in I want to get the message and then we turn that here I want to get I want to talk to the critter and get the response from there, so I say case cat Okay, this is the first instance of pattern matching in the new language I'm using a pattern match to say test if this is a cat And if it is a sign it to a local variable and then call meow on that variable, but also notice that it's it's it's it's saying You don't have a default expression, right? you know it doesn't know what all the input values are but It knows I don't have all the the I knows I need to do something better So I want to give it an explicit knowledge of what all the possible values are and the way I can do that It's by making this type sealed Right, so I now I'm saying this type only allows two particular subclasses cat and dog And so now these types either must be also sealed and permit subclasses or they must be final Okay, so implements animal Local classes must not extend sealed. Okay. Yeah fair enough. This doesn't you can't put it inside the Instead of a method there. Okay there so now I've got these types Okay, cannot be referenced from a static context. Oh, come on. Okay static static Good Not good Good, okay, so now I've got this now what now the system is saying it doesn't cover all possible input values So it's saying okay, you've got an animal, but there are two different animals You need to care about dogs and cats right and so it'll it'll it'll complain if I don't do the right thing there, okay So the compiler is now helping me catch a Bad an implementation of the types of the algorithm that doesn't cover all the possible cases So if you're building something highly algorithmic something very complicated something We've got regulatory or whatever you want the compiler catching these possible states I imagine you're building a system modeling different kinds of loans and taxes and all that kind of stuff You don't want this. This is not like a framework or not. You don't want this to be open-ended You want to know that you've covered every single case in every single instance You want the compiler to complain when you add a new type of loan to the hierarchy? And you've got some logic buried away in some method around you know calculating tax whatever that you haven't covered Now you've got that the compiler has your back and a big part of that is this pattern matching Okay, you've seen this pattern matching before right you've probably seen the new smart instance of right I can actually go back over here. I can say if Felix instance of cat C right So that in I don't have to downcast I can now call see that now inside of that if block. I'm using that smart Smart instance of check there. Okay, but it's in the that's impossible now in this Well, okay, what if What if What if these are? Records right so public final public record right string name Okay public record Static record cat string name right implements animal. Okay So now I've got you know, these are records. They have they have components and Maybe I don't want it. Maybe I want to like maybe I don't want to get the method Maybe I just want to do something with the component fields. Well now. Do you know there's a? nested pattern matching so I can actually say that I want to match cat I want to match dog and you know Whatever the name of the dog is name See that it extracted out the component field of This record so I'm matching the type and I'm saying if it matches Then there's a component field in the record extracted out hoisted out into a variable and then allow me to reference it Okay, this is the kind of stuff you could do in Scala right this is very very powerful for Java I mean it's really powerful for any language, but the fact that it's now here is just wonderful So, okay, so the name of the cat is Whatever name okay, okay, so I've got now a smart pattern matching. Oh here. I got a like Felix There you go. Okay, so the name of the cat the name of the dog So let's see what the message is if I run this program. What do I get? I got the wrong variable there that's for sure Message run that again Okay, there you go Felix I'm saying so all this is oh, you know what? I'm gonna I just realized the Barbie theme doesn't capture It's not making it clear when I have an error. So here we go wake up good stuff So there we go the the compiler working together with all these things Enables a whole new paradigm data-oriented programming. Okay, I really like this stuff This is like very very interesting now suddenly using the switch statement doesn't feel so dirty sometimes suddenly I don't need to have a strategy pattern just to like Handle a number of potential states or implementations to iterate over possible range of values So this is one thing that's really really powerful and it's enabled by the lowly record which we just looked at Okay, so okay good now you understand that let's go back to our regularly scheduled spring boot application Spring application that run service application that class Args good next thing. I've got an application here And the application is just your regular spring boot application. So we'll create a record and We'll say integer ID string name. Okay, and it's just gonna be a spring data record, right? I'm gonna create a repository here customer repository Extends list of crud repository managing instances of type customer whose primary key is a type integer And now I want to use that and of course This is pretty straightforward. I can create a htb controller for example like so pretty stock standard stuff So customer htb controller Create an endpoint get mapping customers collection Customer blah blah blah, you know just you've seen this kind of stuff before right so private final customer repository repository Voila, okay and This dot repository that find all very good. So there's my simple htb endpoint. I'm gonna be using a database obviously So I'll go here I'll say initialize the sequel database and then I'm gonna create a sequel file here Just to have some data with which to work. So I'll go back here Okay, new file called schema dot SQL create table if not exists customer ID serial primary key name var car 255 Same old same old. Okay. There you go. Good now I want to have some sample data. So I'll go here and I'll say data SQL delete from customer insert into customer name Values And you know I've got the great dr. Sire. I've got Michael and Me and whatever. Okay. Good. Whatever just Three's a crowd. Okay. Good. So we've got our data now I'm gonna run this in the day. I'm gonna store this in a database, right? Well, I don't want to have a long wiki page with 500 easy steps to production I want people to be able to take the code get going and run it and automatically have a database So there's a new feature in spring boot 3.1, which we released in May to support test containers for development mode and you can see that when I went to start that spring that I Now it automatically created a brand new public static void main in my test code, right? This is in the test repository not the production code and it's a regular Java configuration class, but it has extra bean definitions for our postgres database Okay, so this is only for development mode so I can run the spring boot application from here And if using gradle or maven there's our there are tasks you can use so maven w spring boot test run for example And that's gonna spin up postgres And then automatically connect to it now the The thing is I want to also use dev tools dev tools are great for re you know for iterating over values or for making changes quickly the basic idea behind dev tools is Java is pretty slow to restart but spring is very quick because all it's gonna do is recreate the beans it loads new Classes in the class class loader and then it recreates the beans This is really fast usually unless you're restarting postgres every single time Then it's gonna be so so I don't want this to be restarted every single time I'll use a annotation From the spring dev tools library called at restart scope That's gonna tell spring boot that when it does a reload a live reload of the code to not restart the postgres container Okay, so I'm still in development mode You can see that this first time when I ran the application in development mode It started up postgres. It's it actually created a docker image and started up postgres for me automatically and it established a connection and It took a very very long time 2.92 seconds right exceedingly achingly Excruciatingly long, but now I've got dev tools going and I don't need to do that again I can just start making changes by recompiling the code. Okay, so let's go ahead and see if that works Let's command-shift f9. Okay. I just did a small reload. There we go So that took 1.1 seconds. Let's try again. Let's create a new endpoint here at get mapping customers name collection of Customers by name path variable string name. Okay, and I'm gonna use this. I'll say refined by name Passing that in there's no method there I'll use the IDE to do the work take that chat GBT and then reload and you can see it's oh, it's still oh it's because I have to start dev tools while the The annotations annotate with a restart scope. Okay, let's try this again System out hello Command shift f9 There you go point two five three seconds right literally 10 x 10 x 10 times faster than the original one right So now I can make a change command shift f9 command shift f9 does recompile right here's recompile good So, okay, let's go here local host Customers voila it worked of course it worked. It was a demo. It was always going to work This is all pre-stock standard stuff But we haven't stayed still one of the things that we just announced at the last spring one way back in in August of this year, which is Last month we announced a brand new project called spring AI right and so if you go here spring projects Experimental it's a it's a new integration with your favorite large language model Okay, and it's very easy to use so let's go ahead and pull that into our application spring AI, okay, and It's experimental mind you just like the language models themselves and so we're going to pull this in Let's see we need to have this dependency Okay, here we are Good. Oh, I forgot the experimental dot AI and we want to this one here version and of course we want to This here right so pull that in Voila, and then we need the the repository here. So I'll add a spring snapshot repository Down here Okay, paste copy copy paste Snap shot snap shots Snap shot Is it plural or is it singular? What did I just have? Okay, yeah, okay fine. Very good. So now I'm going to reload that or not. It'll just oh that's because of this. Oh Reload that Now I've got it on the class path. Let's go ahead and restart Test run because it's a class path change and now I can actually use that right here Okay, so I can say at controller at response body class AI controller Okay, now in order to use this new AI client that the spring AI project gives you You have to configure your your your you know your credentials to connect to the LLM of your choice in this case I'm using the starter for for the open AI. Okay, so I would say spring AI open AI API key right well, obviously, I've already specified this I've got an environment variable. I did something like that I said, you know export this and all that as an environment variable in my shell I'm not I'm not about to leak my open AI Key to you. Okay, so just trust me that it's there and it's fine. Okay, so I've got this here And I'm just gonna make a I want a story. Okay. I'm gonna say okay map string string story time, right? map dot of story, okay, and I'll use the AI client. I'll say hey tell me a story about the delicious food in Singapore in the style of famed children's author Dr. Seuss another one of my favorite doctors along with Dr. Sire. That's him Okay, all right, so we're gonna we're gonna go ahead and have it tell us a story So let's go ahead and do that. Maybe I'll put that I don't know where to put that. Okay, good Command shift F9 right just reload Good point to go back here local host story Okay, and it'll take its time. It's doing its thing. Let's see what it says This is awkward It It's smart, but not fast It's faster than I am I suppose that's fair Wait am I on the internet? Oh, yeah, there we are Okay, so in a land far away where the sun always shines there lies a place with amazing food designs Singapore It's called a culinary delight with flavors so bold They'll take you to new heights in this whimsical land where the foodies all gather There's a tale to be told with rhyme and a blather So sit sit right down and lend me your ear as they take you in a journey both far and near in the heart of the city Where the streets come alive? There's a hawkers enter where foodies all strive to taste the deliciousness of Singapore's fare with dishes So unique you won't find them anywhere or elsewhere first. Let's start with the chili crab Oh, so fine spicy and tangy it'll make your taste buds shine served with mantou buns. What's that? What is that I gathered, but what's the mantel part? What is it's like? What does it mean in Chinese? Bountyful What I yeah, I gathered the I gathered the bun part people. I'm I'm with you on that I'm just trying to figure out the mantel bit. Okay, that's so bountiful. I like that. Okay, Fai Cheng Hao I'm excited about this. I'm gonna go try it out. So Serve the mantel buns fluffy and white. It's a seafood feast a true. I'm not gonna try it It's a seafood. I'm allergic. I should have read the next line It's a seafood feast a true culinary delight. Next we have the high knees chicken rice a simple dish It'll make you think twice the chicken is tender the rice is so fragrant ginger and chili sauce make it truly magnificent That's a rhyme in a way. I guess Now let's not forget about laksa my friend a coconut curry noodle soup that'll make your heart mend with shrimp tofu and fish cake a Spicy broth so grand. It's a bowl of comfort that'll make you feel so grand. Yes So anyway, you get the idea the AI is smart. It's really smart and This is text. Obviously. This is just a human language Text block, but there's a whole category of engineering that has arisen from these new AI models called prompt engineering And the whole point is if you craft if you craft a prompt the text that you send to the model to the AI if you Crafts the prompt correctly you can ask it to give you data in the in a data interchange format that your programs can use Like you can say hey do this thing give me the response in the shape of the following Jason schema And if you don't have an answer say nothing, right? So now you can actually write code to work with the results as opposed to just here's a bunch of text It's really quite interesting and there's a you know if you if you can become a pruned engineer There's a lot of money in it apparently so Yeah, so that's pretty cool But but you did you notice that when I when I made that request it took a long time Took a long time and what's happening to our system while we're waiting for that for that response to come What's happening to that thread? Yeah, it's blocked So the AI is smart, but we're not look at our code just did something really stupid, right? So we need to fix that what think about what's happening here We're sitting there in this thread right here We're there is there's a request that's gone into our web server and we're gonna produce a response and We're gonna produce a response, but we're gonna make a call to this other thing So the time it takes to give that response it was significant and we're sitting on the thread We're unable to move forward and nobody else in the system is able to use it, right? This is a problem. Well, there is a solution. It's it's project loom. How many of you've heard about project loom, right? Okay, so let's take a brief digression to talk about what that is. Okay, we're gonna go here. We're gonna use Java 3.2 Java 21 and Loom Do I owe maven? Yes, because I'm Okay, open this up you a o Loom that zip Loom is loom is an attempt to make that Naive code you wrote in college actually scale right and it's a it's it's Arguably one of the most important features in Java if not the most important feature and it opens up new doors So what I mean by that is let's let's just build a very simple Network service. Okay, I'm gonna build a very simple network service. Let's see void serve This throws exception. Okay, and I'll create a spring bean application runner Returns args service very good. And here I'm gonna create a Server socket so new server socket 90 90. Okay, and what am I gonna do? I'm gonna say while true Except new clients. So I'll save our client equals SS dot accept Okay, and what for each, you know, that's gonna block It's gonna wait until a new client accepts and then once I have a client I want to handle the request So I'll have a method here handle that takes the socket that I've just gotten from the client and does something with it Right, so I'll just I'll call that method. I'll say handle client. Voila. Okay, pretty basic stuff And the inside of this what will I do? Well, maybe I'll have some sort of logic to Accumulate the data. So BA OS equals new byte array Output stream. Okay, very good. And then I've got the input stream, right? That's the socket dot get input stream and you know, I want to process I'm gonna take the data from the input stream. Okay, so try and I'll take this code down here and I'm gonna read the data. I'll say file copy utils dot copy From the input stream. So copy input stream to the output stream. Okay, pretty straightforward And you know, I you can imagine actually how that code would look it looks something like this far negative one while Next this is all very simple college level Java 101 kind of stuff, right? While it does not equal negative one BA OS dot write next. Okay, so I read a bite. I write a bite read a bite write a bite get it Okay, so far. What happens if I'm dealing with a network client and that client's going through a tunnel or They're on slow late Wi-Fi or they're sending a lot of data Well in that read is going to take a long time to return and in the meantime this line doesn't get executed We say that the thread is blocked Which means that if I have more than one client that's connecting to my server here Then the next client won't be able to get a response until the first one is done. Okay, easy solution I'll you I'll quit a thread right pretty straightforward. So var executor Equals executors dot new fixed thread pool run time dot get available processors there you go I'm gonna just for each new thread. I'm gonna handle that For each new request. I'll handle it on a different thread. Okay Good, there's this straight straightforward easily enough easily understood code. Okay now with the problem with this Unfortunately is we're still not out of the woods yet. The problem with this is of course. I'm still blocking the thread I've just got more threads, but I can't create infinite threads They're because right now in JDK, you know in the old days the distant old days before September 19th, 2023 We one thread equal to one operating system thread I meant two megabytes of RAM associated with each new thread that you created So realistically you couldn't create a thousand threads, right? You wouldn't be able to you just run out of RAM first You'd have a lot more problems besides the actual multi-threaded code itself. So What can I do to sort of improve the situation? Well, I want to I want to have some way to Know that I'm blocking and then move off that thread Until there's something for me to do because if I don't have to sit on the thread then somebody else can reuse that thread Well before we had a way to do this. There's Java and IO non-blocking IO You say hey, I'm interested in these bites. Here's a callback. Let me know when it's available and then That was a complete pain in the butt So then there's an IO2 which is also a pain in the butt. So then my friend a guy named trustin Lee over in In Korea he he created neti Right neti is a much better way to work, but it's still very low level. Nobody wants to write Non-blocking IO code. It's just a terrible way to spend the day And so we have higher level higher order paradigms like reactive programming. It's a lot better But still wouldn't be great if this code could work. Why do I have to do the reactive stuff? You might hear people say and and that's sort of the promise of project loom. It'll automatically Move the code off of the thread put it into RAM Make sure that the blocking thing that you're waiting for finishes and then put the code back on the thread Okay, and you if you want to see this in action. It's not that hard. Let's just comment this out, okay? We'll say bean application runner return new application runner and We're going to create a bunch of threads. We'll say threads into stream Dot range zero to a hundred or whatever it doesn't matter and I'm going to create for each new index I'm going to create a brand new virtual thread. Okay Unstarted and I'll pass in a runnable and I'm going to turn this into a list. Okay, so I've got a hundred threads and then for each thread for VAR T threads, I'll do I'll start the thread and then for VAR T Threads, I'm going to join the thread. Okay, so I've got a hundred threads, which isn't a lot But it's enough that I have I have to share I can't have truly a hundred truly concurrent things So let's say I do something. Okay, let's say I do Thread dot sleep a hundred and then I want to observe I want to capture the current thread I'll observe it. Okay, so I'll observed equals new concurrent skip list set of string okay, and This is going to throw an exception, of course, and I'll say and I'll have a field here I don't want to I don't want to sample all of them. Let's just sample the first one So I'll say first equals index equals zero. Okay, so if it's the first Then observed dot add the current thread dot Current thread dot two string. Okay, and then I'm going to do that again. I'll just copy and paste here We go one more time and Then again and then again. Okay, so I've I've slept a bunch of times and then after the sleep This is a blocking operation this sleep, right? I'm sleeping a bunch of times after the sleep. I'm observing my current thread I'm just logging it out so that I know what it is and I'm adding it to a set So I'll only keep the unique ones and I'm only sampling one out of the 100 threads. Okay So now we can run the program again. Here we go run this Come on Did I print it out? Oh, I didn't print out the observed. Okay. Here we go system out observed to the string Voila take three Okay, see that so we can see that in the course of the run I I slept four different times the whole time I was running on the same virtual thread thread number 33 thread number 33 thread number 33 Thread number 33, but each time I was running a different carrier thread a different actual operating system thread It automatically moved it from one thread to another. It's like it wrote a callback behind the scenes in the compiler To run it on a different thread for me after the blocking thing I used thread dot sleep, but input stream dot read is another blocking thing It'll automatically do the same magic trick. It'll move it to another thread after that blocking thing is finished Allowing you to free up the real thread the carrier thread so that something else in the system can use it This is a huge win Right, this is a big deal. So this is a this is project loom. This is virtual threads This just came out like I said a week ago basically a yeah like eight days ago So I fully expect you're all already updated and using the new stuff and if you're not shame on you people are judging you You're embarrassing us Get it done. If unless you hate money unless you really hate money In which case don't bother upgrading Stay where you are. It's fine Okay, so project loom good stuff. Now you saw this code you look at the code I wrote before my little network server Let me reinstate it my little network server Let's let's hide the old one Whatever threads get rid of that I've got my little network server down here and you know, I want to use I want to change my network server to take advantage of Project loom. Well, it's easy enough. I just go here and I change the executor. I say new virtual thread task executor That's it. Everything else stays exactly the same the network server the input stream read all that it's now doing the right thing It'll automatically move the code to a different thread when it's blocking when I do client read But this is a it's still it's very good very convenient But there's a lot of places in your typical spring boot application where you'll have threads so Do you have to do that? Well, you could but if you're using spring boot 3.2 the new stuff We'll do it for you Just enable that Spring threads that virtual that enables wherever we can for example Apache Tomcat. You're you're Messaging an integration layer probably all sorts of where else did we do that? I mean, it's just all throughout the stack basically, you know What at a sink? Yeah, like anything where anything where you might have an auto-configured Task executor will just plug in the new virtual thread pool. And by the way, it's not a very it's not a thread pool The the new virtual task executor doesn't pull it makes no point to pull you can create millions of threads now Right. Don't don't try that on Java 20 But you can create millions of virtual threads today now. They're cheap. They don't cost anything, right? So this is an amazing win Okay, so we've got project loom Here's my application. It's using spring boot 3.2. You kind of understand what's happening here I'm blocking but that doesn't mean that my system has to lose scalability all I got to do is enable that Okay, great. So now I've got my application command shift of nine. Oh command shift of nine reload It's up and running. I've got the story. I've got my data now, of course This is a very efficient application But I'm gonna want this to be as efficient as possible when I was here last time we talked ever so briefly about GrauVM Who we here remembers GrauVM? Okay, GrauVM is a open JDK distribution that provides support for You know, it's open JDK, but it has a few extra nice Utilities the most important those prominent of which is the native image compiler and the native image compiler does an analysis on your code It looks at all the code in your class path and it looks at all the code that that code is using And sorry looks at all the code that in your main method and looks at all the code that that code is using Looks at all the code that that code is using and so on it goes down the line and it finds all the places where you're Explicitly using other classes and it puts all that in a big bag of classes and it keeps those and whatever it doesn't discover It throws away. Well, of course This is a this is very efficient because you're only keeping the types that your system actually needs to run The problem is that it misses some things So for example, if you're doing reflection or serialization or proxies or loading things into the class loader It's gonna miss a lot of that so you have to tell it about these things, right? You have to tell it not to throw away these types That it thinks you don't need and during static analysis at compile time and that's those are called hints You have to give it a configuration, right? And I talked to you about this before you in order to write that configuration You have to have a directory full of JSON configuration files, right? You're saying hey for reflection I plan on reflecting on this class Please keep the metadata associated with this type or when I plan on creating a JDG proxy for these three interfaces Please keep the metadata associated for that proxy in their heap of the native image So these these this requires a lot of JSON configuration There's a couple of problems with JSON configuration this JSON configuration first first It just sounds stupid, right? I don't like saying the word JSON to other adults I just don't feel serious as a person. It just sounds like a stupid thing to say to somebody I don't know why we agreed on that name. It just it just sounds really dumb So I Think we should change the name, right? We we I'm I speak French and French you'd say Jason Which sounds a lot cooler? I'm all about Jason. Yeah, we exact and then and this is cool There but I was in Taipei recently and there they have Jingsong, which means happiness And I think that would be great too either way. I'm done with Jason. Okay team Jison here We are so that's part one part two is there's still a lot of Jison that you have to write. It's very annoying I don't want to write all that. I don't have enough time to do that I don't even have enough time to finish this talk. Why would I want to want to write all that? So there's a new AOT engine in spring boot three that'll do it for us, right? And you can easily use it. You just go here and you do Maven P native skip tests native compile, okay, and You know probably clean for good measure what could go wrong clean package Okay, so that's gonna we're just gonna let that run in the background the problem Of course is is it's doing that incredible analysis is going through all the code in the code class path and Trying to figure out what is being used and what's not in keeping it, right? And it's doing it every single time for all of the dependencies for all the code in the JDK for all the code in your Codebase it's doing this analysis and it's taking a lot of time. Look at this memory by the way look at this memory That's one slack worth of RAM That's a lot of RAM That's like one tab and my Chrome Browser, right? It's obscene. It's obscene way too much RAM So anyway, it's gonna take a long time It does take a long time and it takes a long time so long in fact that I kind of get It's now to the point where I kind of get bored I sit there waiting for their results and it's like it takes long enough that I get stuck I can't move forward but it doesn't take long enough that I can go do something useful like take a walk or fire off a Reply to an email or or go to the bathroom or make coffee or whatever like it's just very frustrating You know and I just get stuck there hand in head just sort of waiting for the response Waiting and waiting and waiting and it's gotten to the point now where I kind of hear Elevator music right here theme song music when I do these native in compilations I just start humming music because I get bored right and and eventually I just I said wouldn't be great If everybody could hear elevator music. So so I went to Oracle and I said Oracle buddy Please play elevator music during the native image compilation process And I said I already hear elevator music in my head while I do these sometimes long-running compilations I just like everybody else to hear it too. Thank you in advance and I appreciate your amazing work And I do by the way. I really do appreciate their amazing work So I went down and then I got some great responses including this one from our friend over at red hat Andrew din He's a distinguished engineer on the red hat Java team open JDK project reviewer Byte men project lead and a growl developer. So clearly this was a great use of his time and he said Please can you make it this elevator music now friends? I'm not going to play this because you know copyright But basically there it was in the 1990s A the first Pierce Brosnan outing as James Bond Which inspired a video game For Nintendo 64 Which in turn had a soundtrack and this is one of the soundtracks from that from that amazing Video game from that amazing movie and it's really quite good So I like the suggestion a very good choice Andrew and then we got another great response here And it says this guy Ivan he says I would add that using beeps in general Not only for native image really helped me to reduce development time. Yeah That's a great idea My rice cooker makes a ding sound when it's done. Why can't my stupid compiler? It's so obvious to me right like this is a great idea Okay, I like the suggestion and then I got another response and this one's from Fabio Neapouse Fabio is another one of my favorite doctors like dr. Seuss and like dr. Fabio is a researcher on the Gravium team at Oracle labs He's a doctor and he's he responded very nice He says thank you for your feature request Josh The problems with playing music during the compilation process is that it's just fixing the symptoms and we've been and are still working on the Cause making Gravium native images more efficient in terms of time memory and CPU consumption Okay, go on anyway. He continues. I have prototyped a dash dash Josh long mode So here's what that would do you go you use the native image compiler, right? And there it says native image dash dash Josh long mode hello world and then it would print out music brought to you by Josh Now this is a picture. This is a picture people don't let that stop you from understanding that there's music being played Okay, take that for granted. There's music. You can't hear it because it's pixels Okay, but imagine so then you can see the the the music brought to you by Josh long You're hearing the great music. You're still waiting though You still want something nice to look at so then it would just print out this right and this is I think what we want Right, this would be this is what we want, right? Yes, exactly. I'm sure I'm sure we'll get merged Any day now so so anyway, I forgot what we were doing. Oh Right. Yeah, okay the compilation it did finish eventually. So there it is. Let's run that Service targets. All right. Voila service. Let's see start that Okay, it failed because of course we're using test containers and the test containers You know, it's just automatically done for me during test time and I didn't specify in my property files I didn't specify spring data source URL JDBC, etc. So I'm gonna go to my Shell here and I'll go to the desktop and I've got a Docker compose file. That's gonna start up postgres I'll say Docker compose up Nope Docker PS Docker Let me get rid of all this stuff RMF Docker PS Docker RMF Docker RMF postgres, how many postgreses do I have? Okay Docker RMF This one maybe okay Docker PS, okay good. So now Docker Compose up we start that very good and now that that's that other now that I've got that I'm gonna run the script I'm gonna set the environment variables to point to the Docker image that was just created and I'm gonna then run the binary that We just compiled. Okay, so very good. There's the application. It started up in point one one eight seconds Right and about a tenth of a second Which is fast. This is it's got a full web server though So, you know, this is not going not where you're gonna use from Grail VM for cypher like serverless You just use spring cloud function and that'll that won't bother. It won't bring you the whole Tomcat or whatever it's gonna be fine. But that's not what I care about actually what I care about is this this is the Process identifier. So I go get the process identifier PSOR says get that that's the resident set size That's all the memory loaded into the binary, but it's it's measured in in kilobytes So you divide by a thousand. So basically that's about a hundred and fourteen megabytes of RAM Right, so now I've got a program that can handle millions of threads And it takes a hundred megs of RAM Right, so why would I use go again? Why would I use anything besides Java? This is an amazing time to be a Java developer my friends. It's an amazing amazing time now We're basically done with our little tour today But I do want to tell you that there is actually a programming model that you can use Behind the new AOT engine that allows you at compile time to act on these objects and to programmatically Contribute hints to tell it what to keep and what not to keep you saw in this case that spring did all the work for you But sometimes it'll happen that you might need to do some work Manually right and there's a nice component model and we don't have nearly enough time to go into all of that But I did want to make it easy for you. So I wrote a little book for you. It's called It's called everything you never wanted to know about spring boot 3 AOT. It's free You can go download it on tanji.vmware.com content white papers spring boot 3 and then just put your email in there, right and you know Details and then you'll get the book. It's like 50 pages. You can read it in the toilet. It's very very quick And then if you don't want to read if you're not one of those reading types that I made a video for you instead And that's also guess what yes, that's it's free and that's here It's a two-hour masterpiece On all things AOT in the spring boot world. Okay, this opens up a whole bunch of new possibilities about it about two years ago the good doctor here put together a Bunch of the the G son configuration to make Gravian work with the Kubernetes Java client Right, and then I took that and turned it into the spring boot 3 AOT Component model and then we contributed that to the Kubernetes Java client project. So believe it or not Clown a clown though. I do seem to look like I'm actually a Kubernetes contributor and and and one of the benefits is that now you can write controllers for Kubernetes Take like 50 megs of RAM and handle, you know, they have like go routine like scale because of project loom, right? This is Java. We're talking about it's just an amazing time to be in a Java developer My friends who learned something new tonight Great stuff. Who had fun? Great stuff. I thank you so much for hanging out. We're happy to take questions. Thank you Yeah, and no it replaces the executors the spring has its own abstraction called task executor But those usually just wrap executors. So like from W till concurrent executor, you know So, yeah, it went if we have a bean automatically configured for you for You know Tomcat for example will now use a virtual thread pool behind the scenes instead a virtual task executor Yeah, what was the question? Yeah Yeah, yeah, if you if you override the defaults in spring boot it'll it'll honor your Configuration, yeah, so I guess the so don't do that, you know, just Take the win just delete the code and let's bring dude for you Next you in the front my friend Yeah Exactly Exactly. So the the question there's two kinds of proxies in the spring universe. There's JDK proxies and then there's the CG Lib proxies, right? Both of them caused trouble for Gravium so for the case of JDK proxies You can actually tell it. Hey, I'm gonna make a JDK proxy with this interface this interface and this interface and this interface And we put that in the G song configuration And it'll make sure that when you try and create that proxy using an invocation handler at runtime that it works It'll write the shim code Into the heap so that it will do the right thing you say I expect to make a proxy with these three interfaces or these two interfaces or Whatever and it'll allow it to work And so when you what same thing so if spring does that behind the scenes and you've registered that in the G song It'll work or you can use the spring component model that I was talking to you about earlier and Programmatically do it and that's nice because it's it's type safe. It's not so fiddly, right? You don't want strings in a G song configuration file When it comes to the CG live ones well, there's a bit more sorcery there But most of the places of that kind of stuff happens happens in spring framework itself So if you're using the proxy factory Builder inside of a spring framework, right the core of the the bowels of the framework a lot of that kind of stuff If you're using smart instantiating bean Post processor is that one? Yeah, if you're just using a if you're using a smart instantiating bean post processor If you implement these interfaces and you're using these places using these things in these well-known places spring will automatically understand That you've got a CG live proxy most of the time So if you're most of the time you're not gonna be dealing with those anyway directly spring will be doing it for you But we we register the hints is my point of it the framework will do it or you can do it with the programatic call back API Does that help? Yeah, yeah, and I like I said, there's a there's a book and a video with your name on it. Oh what oh Because I mean I have a different reason why why didn't I do it? What's the what am I missing? You're kidding. Oh The so I didn't know that so my sequel from Oracle the company that makes the competing database that their Wait, so the company that makes Java and the company that makes my sequel didn't Communicate okay, so apparently my sequel doesn't work for virtual threads. I didn't know that that's a that's very awkward now Virtual it's it's look when I say doesn't work you got you got to remember that when virtual threads don't work It's like an escalator when it doesn't work. What happens when an escalator doesn't work You just get stairs. It's still fine. You can just go up the stairs, right? So in the case of virtual threads not working you just get the same performance as you had before virtual threads with regular threads It's not like you it's not like it's gonna crash the program. It's just not gonna be as scalable, you know Yeah Yes, I didn't know that that's a pity, but does Maria DB work or is it just my sequel? Oh Well, that's a bummer I Haven't used a so as soon as as soon I've always been teen postgres You know always and as soon as Oracle bought my sequel. I was like, I'll never have to use this again Never and I said I have never had to use it again. It's been the best 12 12 13 years of my life Any other questions comments feedback tomatoes thoughts, so like Should I go back to Barbie? So much better. Yeah, you in the back you say again. Yeah, I mean in general it's right with So if you're doing there's a few gotchas in virtual threads like if you do input stream read inside of a synchronized block then You're you cause a problem. So if you It's not but when I say it doesn't work in that case like I said It just goes back to a regular thread performance because you end up pinning the thread You end up pinning the code onto the thread on which it's running So it just goes back to the Java 20 Kind of thread as opposed to virtual But it's easy enough to get around that you just don't have synchronized blocks around input stream that read Or you do the input stream that read and then you do the synchronization, right? So I guess they just haven't done that that touch up code. It's not It's it's not fun, but it's not It's not like you have to refactor everything. It's just it should be pretty straightforward to kind of identify these these contentious Yeah, exactly. Somebody's got to just file a ticket and spend a weekend and go through the code base and find these obvious Places and we already had we had some of that in spring framework like five six years ago But when we went into reactive We went down the reactive rabbit hole. We got rid of most of that in if not all It's all gone, right? So spring doesn't have any of these Contentions contentious sort of blocks in the code anymore for years. I Didn't know this is I don't use my sequel. There's a reason I'm so I'm so excited to have another reason not to use it anyway. Go on any other questions Yes, you in the front if you hate money if you hate it if you really just don't want any of it Don't use virtual threads There is a little bit of cost you're right. There's there's thread switching context switching so in theory in theory The the per transact on something that doesn't block in theory if you invoke The context switching, I mean it wouldn't do that actually if you have something that doesn't block It's not gonna switch the thread in the first place But in theory there's it could be a little slower per transaction, right? But but again, it's you'll get so many more transactions at the same time now You know, I don't know do your own benchmarking as always but Yeah, I don't know the cost I haven't found the cost yet Yeah, oh, yeah, so he's just made a great point instead of like a megabyte per thread. It's like 10k or something like that It's not zero, but it's so it's an order of magnitude less or many orders of magnitude less, you know Yes, sir with the yellow shirt. I'm sorry. I'm not understanding because there's a air conditioning going off and I can barely hear anything Yes, so so reactive programming gives me three big benefits, right? One is ease of composition two is you know, let's say scalability right the same thing as loom and then three of course is error handling, right? So what I mean by that is, you know back Pressure and retries and all this kind of stuff when I use a reactor So this is the thing that most people care about is the reactive the scalability How many requests can I handle at the same time? And in this regard, they are the same loom and reactive are basically going to give you the same results But this is still huge if you're writing if you're doing service orchestration and composition if you're doing Scattered gather kinds of stuff, then you want reactive. Trust me. Have you you know, do you know who Tony whore is? I'm forgive me if I'm butchering his name. He's the fella who Came up with the one billion dollar mistake, right? The knowl knowl is the one billion dollar mistake But in the 70s he was talking about also how threads are just a terrible way to do concurrency, right? The way we think of the threads as we have them in Java It's a it's the same basic program model from last 50 years And he says it's just not a safe way to do concurrency, right? There's there's only one person who truly knows how to write safe multi-threaded code in Java And it's not you that's the point. I don't know who it is. Nobody does but it's not you, right? That's the point there are better ways to describe state transitions in a concurrent system then Java lang thread much better ways concurrent sequential processes actors sagas, you know Actors what's the other one reactive programming right all these things are much safer because they Hide or make it so that you have guard will so you don't get in trouble with concurrent state And right now you don't have none of that there is a new API coming in project loom in future versions Right, they actually delivered just one of three tranches of project loom GA right now is the most important part Which is the virtual threads, but they're also working on shared scoped like a like a more memory efficient thread local right called scoped values, and then they're also going to deliver a Program model for making it easier to work with threads because again threads are just a great way to point something at your foot That you don't want pointed at it, right? Even if they're and now you've got millions of things pointed at your feet, right? It's it's not better Definitely not and you want the framework to do that stuff for you And then error handling reactive programming gives you back pressure It gives you error handling it gives you a way to deal with the realities of distribution and so yeah if you're building a monolith and Then then maybe all you care about is this Right in which case yeah, please go for it, and if you've got existing code, and you don't want to refactor Maybe this is enough. It's not like there's no way to handle some of these error handling You can use spring cloud and spring retry and the circuit breaker pad all that stuff you can add these extra jars and get some of that Robustness, okay. I'm not saying it's impossible. I'm just saying it's really nice and built in by default with reactive programming So if you've got existing code, I wouldn't convert it away But if you've got existing servlet code and JDBC code, yeah move to loom and Probably fine. You're probably in a good place and certainly you'll save money Cheers great question Any other questions? Yeah Yes, but it's Remember threat locals create an object for every single thread and now you can have millions of threads So you can actually run into memory issues where you couldn't realistically do it before right? That's what that's what the scope values thing is all about and that's not here yet. So just be aware What was the other was there another question? Yeah, can you use MBC with virtual threads? Oh Not no, but you don't need to there they give you the same scalability Yeah Like if you in theory if you're using all servlet async if you're doing Yeah Right it's only if you got blocking code, which is statistically like 90% of the code out there But if you if you're already doing reactive there you gain nothing from virtual threads, which is fine That's you did the work. You got your scalability five years ahead of time. You're happy you're in production Good stay there, but the rest of us who have got existing code. It's not reactive. This is a big deal. It's good free win Thanks, Java any other questions Yes, sir Yeah Continuations Is there is what I don't know good question Yeah It did some compiler magic. I don't know mine is not to wonder why Smarter people than I have worked on this. I don't know. I'm sure it's fine I like if they can't figure it out, then it can't be figured out. Um, I Don't know though. Yeah, please find out and let me know anybody else Yes, there's I don't know the first one in the front and then the one in the back. Yes. Oh Say goodbye to write once run anywhere and say hello to scalability Um, yeah, it's it's it's true. You do but like and the worst part is you can't do cross compilations So you can't compile from Mac on Linux and compile for Windows on Mac or whatever But for example GitHub actions, you know or something like that They usually have matrix builds so you can say run the same pipeline on Mac Windows arm Linux, whatever that's part one and then part two is if you're using build packs we have build pack support if you're using build packs on Linux on GitHub actions or something like that to build your code. It'll actually Run the code if you or even on your Mac if you're running build packs on your Mac It'll run the code inside of a Linux container, right? That's what Docker is and then that native binary will be a Linux binary even though you compiled it on the Mac It'll be an arm or an Intel Linux binary. So be aware of that. You don't want to try and deploy your arm Linux binary to a x86 Linux server, but yeah, you can kind of cheat that way by having build packs since most people are going to Linux Eventually, that's probably enough, you know only for the compilation. Yeah It's at runtime. It took a hundred megs of memory. I'm not understanding my friend. Oh, is it faster? Oh, I wish yeah Yeah And they're there by the way, there's two versions of gravity I'm there's the open source one the community edition and the Oracle one and they add some cool features there and the the commercial Enterprise one first and by the way, please go buy that like Java's great oracle needs to make some money for it. If that's how they do it. I'm fine with that just Go go, you know, especially if it gives you a fast feature or whatever. I don't know whatever Yeah, what I don't know They are quite smart. Yeah, the SR the Jerry But but actually very importantly it only includes what's needed so from a security posture You don't have everything in the class that you've only the things that are being used, which is nice So everything from the JDK. It's not included only the things from the JDK that you use So those Corba libraries all of those got removed but like, you know, whatever. What's a All of swing. Yeah, that's not in my the native image doesn't have that code I couldn't run it even I even if I wanted to obscene. Yeah. Yeah, but I mean, it's already obscene with the JRE version too So, you know, well, there's one there Jit comp. No, but you can do profile got it optimizations and Oracle or that's a feature in the enterprise one, right? Okay Oracle will say that profile got it optimizations are faster than jit can get to right. It's a different way of Optimizing the code you do it at runtime and you say it off to a profile and that gets fed into the Compilation I guess it but uh, but I think it's a feature of the X you have to pay Yeah That was just in their blog that just came out like last week You could check that when the Java 21 support for Gravium came out. They talked about these numbers very interesting They're they're saying at the highest peak performance of Gravium. They're now faster than the JRE at the highest peak for performance for the expensive Not, you know commercial not open source one For some use cases. Yeah, big Kevin into our big asterisk, whatever Any other questions? Yeah I don't know. It's just um, let me see. Yeah Serial GC What is that? garbage collector serial GC There you go. Oh, I think that Flickering light is telling us it's time to wrap it up Can we get a selfie? They're okay. Can we get a selfie together? Okay doc. Come on. Oh my god. Oh my legs