jPrime 2026

Observing Project Valhalla, Cay Horstmann

49:05 · 03 Jun 2026 – 04 Jun 2026 · YouTube

About this talk

In this talk, Kai Horstmann discusses Project Valhalla, which aims to introduce value types in Java, allowing for more efficient memory management and better performance. He contrasts primitive types, which are stack-allocated for efficiency, with reference types stored on the heap, highlighting the need for a better solution for creating value classes that maintain performance while providing type safety. The speaker explains the syntax for creating value classes, their behavior, optimizations enabled by the JVM, and benchmarks comparing their performance to traditional object-based implementations. He also touches on potential issues like nullability and tearing in concurrent environments, as well as future enhancements to Java, including the bang syntax for non-nullable value types. The session emphasizes the importance of understanding the performance implications of these changes and encourages the audience to experiment with the new features as they become available.

Full transcript

Okay. As always, 2 minutes late. This is okay. So, with the last people, just close the door. Yeah, thank you so much. So, I want to begin with a very, very scary story. I have been coding in Java for 20 years without agents. I used to write code in Notepad. Java code. And this code this code did compile from the first time. So, I consider myself as

a guru, as a master, as whatever you like it. Yes, it was hard, but it was amazing. The times have changed. But, by the way, do you remember what type of a conference this is? Who will say? Java Java conference, correct. And finally, at the end of day one, we have a talk about Java. Yes, and I am I'm so much happy that we will have the

talk especially about this amazing project Valhalla from one of the most amazing speakers in world, and I say like right word, Kai Horstmann. Please all welcome our good friend Kai >> Well, thank you very much for for the kind words. I hope I will be able to live up to them. Um, so if anyone wants to follow along the slides later, just snap a picture of the

URL. It's It's a web page. And also there's a link to download the source code if you want to experiment later. Um, so yes, I'm Cay Horstmann. My claim to fame is that um, 30 years ago um, I wrote a Java book that's is now in its 14th edition. And so ever since I have been following with what goes on in the world of Java and try

to explain it to people. Um, so without much further ado, we're just going to go right into Project Valhalla and what it's all about. As you know, ever since Java 1.0 from 30 years ago, there were eight primitive types that are different than all the other types, right? Normally types are reference types. Um, the objects live on the heap and there's a pointer to the object. And

as you can see in the picture, here you have the reference, here you have the object. The reference can live wherever, but the object actually lives on the heap. But these primitive types, they're different. They are kind of just like those references. They sit right there in the stack or inside another object. So it's it's kind of weird that we have this dichotomy. Um, and it's of

course it was efficiency, right? Every reference um, sits somewhere, so you need to have an indirection to it that takes space. And of course um, then there's an object header that takes space. And so >> [clears throat] >> the primitive types are performant because they don't have that. There's less memory. Also, if you have a bunch of Let's say that you have a bunch of XY values.

Um, and if you have them all in one array of doubles, then they're all locals. If the array gets fetched, uh, you know, it might fit in a cache line or two. Whereas if they're all objects that are sprinkled all over the heap, then if you access them one by one, then locality is bad and your program will run more slowly. Of course, also objects are garbage

collected and garbage collection, as we will soon see, takes some amount of time. And so, it would be kind of nice to have more things that are just like the primitive types. And so, they are called values. So, a value is something that does not live inside It's not an object. It's not allocated on the heap. It is either allocated on the stack or inside some other

object. Why might you want them? So, there's also such a small things such as currency values, measures, centimeters, something, date time. Um people want short floats, long floats. I'm going to be using points with XY coordinates, um tuples, whatever. Um another thing that people want is um sometimes you have a domain-level class, like you have a user ID and you store it in a long. You would

never make a class user ID to store a single long because that's kind of expensive, right? Then, if you have a lot of these IDs, then each of them sits separately. But, you On the other hand, if you make it as a as a long, then you lose type safety. So, there's a tension. And so, it would be nice to be able to have more of these

classes. So, the motto is that you want something that codes like a class but acts like an int. Um we've wanted uh been wanting this for at least 12 years um when the project Valhalla officially started. And sometime in the next 12 years, hopefully, we will see the fruits of this um that that you can actually touch. Supposedly, it's pretty close. Um but over the years, if

you look at some some older uh videos, you'll see the surface syntax has changed many times. There were many different implementations. And a couple years ago, um the folks at Oracle decided that now they have everything in place. And so, what I'm telling you today is going to be what comes out. The question is only when does it come out? Um So, let's just dive into the

basics. Um, so, if you look at DEP 401, that is the foundation of value classes, and it's actually very simple. When you make a value class, um, and let's see if I have some some code here. No, I'll just show you the code. Um, here I have a value, well, it's actually a value record, and you see the keyword value, and otherwise it's exactly like any other

Java thing. Um, and you just write value in front of it, and that has some important, uh, consequences. That, uh, first of all, the instance fields are final, um, kind of like with a record. Um, it also means equals and hash codes are derived from the fields, um, just like with a record. The methods are not synchronized. Um, I'll get to that in a in a minute.

And the JVM now looks at the things as it's declared as a value class, so I can go to town and optimize it in any way I want. The VM makes no promises. It doesn't say it does and say I will optimize this, but it can take the opportunity for optimization. So, the idea of a value class is to say that the as a programmer, for you

they are just like values, just like 42 is a value. If you have a temperature or something, you don't care where it sits in memory, and so it's the same thing. So, often times value classes will be records, such as here our value record point that you've just seen, and you can see it has a method. Also nice that, uh, yeah, you can, uh, it has a

natural home for behavior. Um, and it's something when you have a point, you don't care where it sits. Also, it means if you have a point with coordinates zero and one, and you have another one with coordinates zero and one, you don't care whether they sit in the same location in memory or whether they are duplicated. That is entirely up for the VM to do. Um, I'm

not going to get into the details of abstract value classes, but there are such things. Um, you can look look it up in in in the DEP. So, all we're going to be talking about today is uh, observing and performance. if you want to build these, um you can actually um experiment with them today. Um there is uh uh Mark Hoffman on his uh excellent Java Almanac

site has a has the latest build running and you can play with it uh and just you know kick kickstart the syntax, you can maybe get a recent build on on this website from Oracle. Um whenever they run a major conference, they update it. Um and [clears throat] really, if you want to play, the easiest thing is just build it yourself. It is not hard to build.

It takes 15-20 minutes um after you've downloaded the dependencies. Um I um you can build it on Linux, you can build it on on Mac, and I have seen with my own eyes Jose Paumard build it on Windows, so it can be done. Um >> So, you know, just go ahead and build it if you want to and then you can go to town. Um and if

you use SDKMAN and if you use more than one uh JVM, you really should be using SDKMAN. You can just tell SDKMAN to uh that from now on you want to use Valhalla and and there it is. So, let's let's just run something with it. And so, the value syntax is supported in this Valhalla build. So, I'm just going to use value record, value class, and I'll

be good. Um and uh it is a preview feature, so I have to use this the enable preview uh flag, which I always forget the first time. Um so, let's just try something. We now have a shiny value class, the value class point. A point is just a point with XY coordinates, two doubles. Um and uh let's see if it does anything different. Um it's immutable, right?

Because that's a values are immutable. So, if I want to change the point, I have to generate a new point. My method here, the move by method, does that. It gets an um And seen the the code um It gets a displacement in X direction, displacement in Y direction, and it produces a new point. So, I'm now going to do this a lot of times. I think

in my case in my example iterations is a hundred million times. So, I'm going to generate a hundred million new instances of this point by moving it randomly in X direction and Y direction. And trivia fact from physics is if you have like little pieces of little flecks of dust in a liquid and they get banged at by the atoms of the liquid so they get displaced

a little bit. They will actually move by some some amount. So, there's there's some theory that says that they the random movements do not cancel themselves out, but they they actually move about. So, we can try to see after a hundred million operations and then I you can just time it, right? And so, in this particular example I did it with value classes and it took seven

seconds. Then I did it without value classes by simply removing the value and now it ran into in eight seconds. I was not impressed. So, let's dig into what it is that Valhalla actually buys us in a scenario like that. So, [clears throat] first of all, let's focus a little bit on object allocations. The traditional objects have a header 12 bytes before compact headers. We have two

doubles so that's 32 bytes for those. And then you need another four bytes of padding and it gets you a total So, so two doubles is 16 bytes. So, it gets you a total of 16 plus 12 plus padding of 32 bytes. We having a hundred million of them, that gives us about three gigabytes of objects. Now, each of those objects, if we look at the the

program that I have here, here we have it. Each of these is super short-lived because we make one, then we make another one and replace the previous one. So, each of these objects lives for one iteration, is overwritten by the Um so, if I don't use Valhalla, I really do generate um 100 million objects. And you can see that pretty easily. You make a flight recording um

with uh this flag, and then you load it into your favorite flight recording viewer. So, my I have here um well, as it happens, a Zulu Mission Control, and I've already preloaded it here. And so, when you look at the memory tab, then you can see the memory allocation. First of all, you see here that indeed about 3 GB of these point objects were created. This is

without Valhalla. And you can see here the garbage collector uh you know, the memory goes up, then there's a garbage collection phase, and so on. Of course, all of these objects, since they all they they live so short, they get instantly collected, but it is work for the garbage collector to do that. So, if I now were to do this without Valhalla, so let's look at the

other where is it? Here. Oh, here. Um Now, with Valhalla with Valhalla, what am I doing? So, here we have it with Okay, there's nothing happening. Notice, there's no objects allocated. Wait, there's a few, and we'll talk about that in a minute, but you see no garbage uh activity. Why is that so? Because in the code that you see here, there's actually no new object generated. Even

though it says in the code of move move move by that it makes a new point, with Valhalla, it says, "I don't need to allocate it on the heap. I just locally allocated and then I replace a stack X and a stack Y by another stack X and another stack Y. And so the garbage collector is completely silent. Um well, that's nice, right? Um and so you

can easily see that by simply making a uh a flight recording. So, we can see more because [clears throat] the next question that I had is okay, that's fine, but why isn't it faster? Right? I mean, it the the difference in speed was was laughable. Um and so one way to do that is you can look at the generated code um and so you do that by

uh by recording the jit activity. Here you see um uh the magic flags to do that. And then the easiest way to analyze it is to log it into this uh to to run it with this jit watch tool. So, I have that here. So, let me first of all open it without Valhalla. Um the user interface is what it is. Um >> um there is a

a Valhalla bug here that means I have to close one of these windows here. So, if we Actually, that Yes. So, so first of what do you what do you see? You see the byte codes in the middle. Um on the left you would see the source. Uh the the >> this is a Java FF FX application and when you run it with Valhalla, there is there's

a weird bug, which is why I can't show you the source at the same time. On the right-hand side, you see the the assembly instructions. As you can see, I run X86. Um and I know not none of us can uh very few of us can read assembly at any speed. Um and so we're not actually going to go into much of the assembly today. Um but

I'll I'll show you how one can tell that something interesting is going on. So, what we want to go on is we want to see this thing with the object allocation. So, in the no Valhalla version, um over here I will see the constructor for the point and um it actually does get constructed. In the with Valhalla version, this will look nice. Um you will see uh

that the construction has been um so so JitWatch nicely crosses it out and says it's not happening. And so, when you mouse over, it'll give you a little comment that says says, "Come on. Have a fix. You can do it." It says um that the Jit has observed that this thing does not escape the method. It can put it in the in a register. So, that's nice.

So, with Valhalla, it found an optimization that without Valhalla did not find, and that makes a difference. Um and also, when we look at the generated assembly here, we can see that actually there is a lot of very active inlining going on. So, um uh all of the method calls are in fact inlined. So, here we see can see here the code for for invoke ver- for

for uh move by, and it is inlined. But what we also see that the random method is inlined. And what you see on the right is the assembly instructions for the first call to random and for the second call to random. Each those are about 80 bytes worth of Um and so, they they may be 20 instructions each. And that is why you don't see much of

a difference in performance. That the random swamps everything else. And so that means that this was a was just not a good benchmark. It didn't show us very well uh what to do. And so one has to to always pay attention to that. So what one really needs to do, of course, is to actually do a decent benchmark. And so the way you do a benchmark is

with this microbenchmarking tool. Um so in case someone wants to try this at home, I just gave the magic uh instructions here. Uh we're not going to do this here. But I did a benchmark. And so what I did is I said, "Well, the randomness, you know, that that uh was not something that had any interest in. I'll just find some other way of jitter these points

around." So instead of randomly kicking them, I said, "I'll just transform them." And first I thought I'll swap the X and Y coordinates, but I said, "That's my probably not be good enough." So I added one to the X coordinate, subtracted uh a half just from the Y coordinate, and then swapped them. And said, "You know, that way it'll just somehow one of them grows a little

bit more in the one direction than the other." And that's it doesn't really matter. I just wanted to have 100 million points transformed in some way. And so then I ran the same benchmark. And now look at this. um first of all, you know, how do you run a benchmark so if if you haven't uh seen that a lot. Um actually have somewhere I have some some

slides I'll point you to some slides for benchmarks. Um so so here's here's what a benchmark looks like. It really is just the code that you would normally want to test. And then you annotate a little uh little bit to give the nature of what you want to uh do. Here it's super simple, just the defaults. And then you tell JMH to run it. It'll run it

many times. It takes uh minutes or for for longer ones, hours to run these benchmarks, uh depending on how many variations you have. It'll warm up the virtual machine so that the JIT really kicks in. And uh because it'll take some number of iterations for the JIT to do its transformational magic. And so here, look at these numbers. So with without Valhalla, it took Yeah. >> 60

uh uh uh uh milliseconds per iteration. And with Valhalla, nothing. So, that's amazing, but it's a little too amazing if you think about it. And so, I uh I stared at this with wonder, and then I realized HotSpot realized that my transform, when you call it four times in a row, it does nothing. And then I Then I actually computed it, you know, on a piece of

scratch paper, and yes, indeed. So, this this transformation was un- unhappily selected. So, what HotSpot realized is that when trying to unroll this loop, which was, you know, a very heavily used loop, so it it really tried this. It said, "After uh So, unrolling means instead of doing a loop and then jumping again to the top, it'll take the code of the loop, take the code of

the loop, take the code of the loop now some number of times." And for some reason, it did it four times, or maybe eight times. And then it's then uh it ran through it in its optimizer and said, "This loop does nothing." And so, it simply cut it out. No wonder that it was very fast. So, that was not a fair benchmark. So, uh the remedy is,

of course, to do something that that where the loop needs to be executed. So, when I did that, finally, by pre-computing the random numbers and then running it, then you get a a perfectly solid result. So, at this point here, we um this is something that one can say is a true benchmark. So, without Valhalla, it it takes um 62 milliseconds, and with Valhalla uh Valhalla, it

takes a quarter of that. Now, that's that's a good performance gain that you might want. And that's as good as you're going to get it because, you know, here we're replacing all of the the object stuff with just X and Y values in on the stack. And so, that's fantastic. That's the kind of performance that you of course in not your entire application is going to be

like that, but it gives you an idea of what performance gain one can do when one doesn't have these these very simple kinds of computation. So, this is great. And remember lower memory footprint is good, too, because uh you don't have to to pay for the memory. All right. So, now I hope that everyone is excited that this is something that uh could be useful. So, let's

get into more of the nitty-gritty. First of all, um when you have a point with X and Y coordinates, um it might surprise you that it's still okay to uh assign it to null. But, that's how Java works, and Java objects can be null. And so, these value objects can also be null. Um but, it's a little unhappy, right? Because we think of uh of a a

value point, there's an X and there's a Y, and that block of X and Y just gets stuck somewhere in memory. But, now we also have to worry, well, what if that thing could be null? Um that means that actually, you know, memory representation now is not so great. Now, you have to say, "Okay, there's I have to have an indicator that says, is it null or

isn't it null?" Could you somehow smuggle it into the X's and the Y's? Not easily, right? They're they're both uh float doubles, and there's no like special double value that somehow invalid. And so, you really need to have a value point you uh you need in your flat representation uh the X and the Y and one bit somehow to indicate whether it was null or not. So,

that is uh how it is. Um and so, yeah, somewhere you need to put it. Um now, if one somehow knew that it was never null, um then of course, one could optimize. In the case in the program that I just shown you, the um the the JIT knows it can never be null, and so it it does not bother to to deal with it because it

knows exactly where all the points came from. But, if you have a point object that sits inside another object, then the JIT does not have uh cannot reason from local knowledge. Or, if you have an array of points, then the JIT cannot know. And in those cases, then uh as we'll see a little later, then uh it really is a hassle that uh one has to kind

of waste uh of these knownness indicators. What if you knew that a point is never null? And in I mean, in many uh situations, you just know from the logic of your code that a certain object can never be null. So, then you can indicate that to uh to Java by saying point bang. Point bang origin means that origin >> for sure is guaranteed never to be

null. So, of course, then you need to initialize it because otherwise, it would be null. And if you don't initialize it, then it's not going to work. Um so, if you later assign it a null, the compiler says that's a syntax error. It will not compile. Also, if you sneakily try to do it at runtime, you know, which you could by the following strategy, that somewhere you

Here I have an array of point bangs. So, I have a path of points, none of which are null, I say so. And then I take that reference into an array of objects. That's something you're always allowed to do in Java, so you're still allowed to do that. You know, you can always take an array of anything and uh cast it into an array of objects. And

then when it's an array of objects, and I put in null, well, the compiler can't complain, right? That's perfectly legal. Um but, the runtime system will. So, if you store a null into an array that was declared to be non-null, you get an array store exception. Just like today, if you have an array of strings, cast it to an array of objects, put in something that's not

a string, you also get an array store exception. So, uh it's just a bit of extra instrumentation for the VM, but nothing really unheard of. This, by the way, will come to all types at some point, not just to to value types. So, some of you may have seen that there was a talk about J Specify earlier today. So, whatever what J Specify does today will at

some point come to actual Java just with this bang syntax. So, it's different from the Kotlin syntax where you had like a question mark where null could be allowed. With Java, of course, 30 years of of Java coding, we can't do that. So, it's the opposite thing that the bang will say it's null free. So, this will probably have an effect on on every coder whether or

not they use value types. So, next thing is initialization. [clears throat] So, when you have a local variable, they always need to they must be initialized. But, if you have fields or array elements, then the initialization by default is null. Well, what if they're not null? Um, and so, let's say you have a local date bang. You can't initialize it to null because it's a bang. You

can't really initialize with all zeros like you could with with a with a double because what does the all zero bit pattern mean for a local date? I mean, you would then have to figure out what exactly goes on inside a local date, you know, which I don't really remember. But, and then is it something meaningful? I think it probably means like January 1st, 1970 or something.

And that doesn't sound like a great default. And so, um, there's [clears throat] now a a new rule that says that these fields, they need to be strictly initialized, which means but they need to be initialized before calling super. So, if you have a class that's that has a superclass, then you you first need to take care of the initialization, then call super, which is the the

opposite of how things used to be. And it's actually a much better way because in the past there was always the possibility that when you call super that some method of the super class then calls some method of the sub class that would see the object in a partially constructed state. So, that has been outlawed for value classes and it's also going to be a good practice

for non-value classes. So, just just keep that in in mind. With arrays, um one has to do something different, namely when you declare an array of bangs, you have to fill it some way. You could either fill it with values that like in the thing that in the way that we've always done with explicit values. Um but what if it's long? You don't want to have to

you know put in a 100 defaults. Then you know maybe they the syntax has not yet been settled on. Maybe you'll just give the initial element and it just one one specimen and it puts them everywhere. Or you know what if you wanted to initialize each of them with something different? Now, it has been proposed that maybe a lambda could do that. Um we don't know what

the syntax is yet um because this is not actually something that has yet been fully implemented. But what we do know is we need some way of specifying what those bang elements are so that they don't get all initialized with null. So, the idea is that the default zero bit thing can never be observed. Fine, it will be so. And in fact, this is already so that

this assignment before super is now legal for all classes, not just for value classes and it is a good idea. So, how can you try any of this stuff given that none of this that there's no syntax for it yet? So, um you can um you can use the bang if you build the B B stands for bang the B world branch and it has limited support

for bang. Um but not very much. Also, if you want to use any of these more interesting features, you can use annotations. So, for example, instead of the bang, you annotate a field as null restricted, and then you can try it out. So, you'll see that in my sample code. Um so, there's a few other annotations, and these annotations, they change over time. Um also, to to

actually use those internal libraries, you need to put some god-awful flags under the for for the compiler or or in your Maven builds. I'll put them here. >> How do I know about all of this stuff? Because if you look at the the build, there is a um there examples and tests. And if you look at the examples and tests, then you can see what the syntax

to use is. So, if you want to try this out, and that's that's the place to find out. All right. So, we've talked about um flattening. We've talked about um the the bangs and null, and that you need to initialize things that are bangs. The last thing that one needs to worry about with values is uh what's called atomic updates. the VM spec says that if you

have a a 32-bit value and you assign it to another 32-bit value somewhere in one thread, and there's another thread that might interrupt the first thread, that nevertheless that assignment is always safe. On the other hand, if you have a 64-bit value, like a long or a double, that assignment is not guaranteed to be safe. And for 20-some years, when uh 32-bit VMs were ubiquitous, in fact,

it wasn't. So, if you've been coding Java for a long time, maybe you have worried about, you know, what if I assign one double to another? It's potentially dangerous if uh there there's a thread preemption. Um and nowadays with 64-bit VMs, of course, no one worries about that. Um uh when we build our own value types, they can be longer than 64-bit, like my points are 128-bit,

and so they can what's called tear. So, what does tearing mean? Um if I have a one thread that moves um this uh XY uh point into a shared point, the one in the middle. And you have another thread that also does that. Then it could theoretically happen that the first thread gets interrupted after having written X, but before having written Y, that thread two gets activated,

overwrites the X and Y, and then thread one gets reactivated, and picks up where it left, namely writing the Y. How could you notice that? Um if if these points had some special properties, I'll show you in a program with that in a minute. So, it is possible then to have objects that are in this uh state where they're half filled with uh with one thing, then

uh with the other, and then the first one goes in there, and so that could of course destroy some invariants. Um is that a problem? Well, if you think that it's so important for you to be able to quickly copy points, then you can say this is important to me, and so the current straw man syntax is that you implement a marker interface called loosely consistent value.

That syntax might also change, but there will be some marker to say, "I know these things, they're big, fat, they could tear, but I don't care." Um and that's at that point, when you do that, then of course the users of those classes, they're on the hook to know that those are somewhat dangerous and uh to >> uh in a concurrent program. How dangerous is this? Well,

reflect back on on your own life 15 years ago. Okay, not all of you have been programming Java for 15 years, but yeah, just say if you did. Were you living in fear of using double? Most people probably not, so that means this is a a somewhat technical thing. So, I don't want to go too deep into getting people worrying worried about that. Can you observe it?

Well, it's easy enough to observe. So, I'm going to a program here where I do the following. I have a bunch of threads and uh I have a shared point uh is here. And I have for technical reasons I had to put it inside another object. Um So, but it's shared by multiple threads. I'm assigning into it points that have the property that the X and Y

coordinate are the same. Then I'm reading it. And if I notice that the X and Y coordinates now were not the same, then I'm giving you an error message. uh you one would hope that that error message is never shown. But actually, when you run it then you will every once in a while see an X and a Y that are different, which means that after writing

the X in one the first thread got interrupted, a second thread put something else in there, and then eventually the um the first one completed. So, these things do actually happen. Um and that's when I said I'm okay to tear. What if I'm more strict and I say I don't want tearing, I'm just going to do the default safe, then none of this is observed and you

can really see that. There's no corruption at all, but look at the time. It is 10 times slower. No, uh 100 times. It is Yeah, it's much much slower because in order to prevent the tearing, there has to be some kind of a synchronization mechanism. So, there is a performance issue about tearing that is kind of uh that's definitely worth thinking about. But again, don't don't overthink

that. You have been willing and able to use double for many years. Uh in 32-bit VMs. Last thing. Um the real value in in terms of um memory savings is if you have large arrays filled to with lots of these values. So, the classic example is I want to have a humongous array of points. I want to have whatever. 10 million points are stored in an array.

And so, what I want them to be is of course just tightly packed XY XY XY XY. I do not want to have a lot of references to individual objects. I want them all just flatly in there. And also, of course, I don't want to have any indicators in there. I do not want to have 10 million null indicators if I know they're all not null. So,

in pretty soon you'll be able to say point bang array, but right now we're not there yet. In in order because there's not even syntax for for this. So, there is a a hidden mechanism to generate one of these. So, I say, you know, I I want this no nulls. I don't care about tearing. I want it from the point class. I want this many. And each

of them initialize with this with this exemplar. And now I'm going to get my my array. And then I want to kind of know, you know, how big is it? So, here just for the so that it doesn't get optimized away, I do some transformation on it. And then I just want to see what happens. So, I just run this thing in case you want to try

it at home. Here's the command line. I then So, here I just put it to sleep so that afterwards I have the time to run JCommand and get a a heap dump. And here is what the heap dump looks like if I didn't do it up here if I had the classic no flattening thing. In this case, you will see that I get whatever this thing is,

100 million instances of point. And in this tiny picture here, you can see these point instances. And you can see I have an one array that is 400 million bytes long. Why four? Because it takes four bytes per reference. So, this is before Valhalla, classic, lots of individual objects, and an array of references. With Valhalla, check it out, get one array. I don't get individual objects at

all. Well, that's that's the promise, right? So, now you get an allocation where you get the header, you can So, that's why you can't see the 16 here. It's actually 12 bits and 4 bits alignment. And then you get act the XY and so on packed in here. Let's see if we can make sense of the 16. Well, every double is 8 bytes. So, another 8 bytes

for the for the Y. And then we have, you know, a gazillion of them. That's the 16. And the the the tail at the end is the header. So, so you can really, you know, with without much complexity, this program here is is tiny. Um and you can immediately see that the memory allocation inside an array is the way that it works. The only blemish is that

we we have to use the secret way of constructing it because the point bank syntax does not yet exist. But to add that syntax should not be a great complexity. So, and so this part here, you know, is if effectively real. The um is able uh not not just it, the VM and the JIT are able um to do this kind of layouting today. And so, people

who, you know, work with large arrays of numbers, they really care about these features greatly. So, let's see if we learn anything. I'm going to make a small change to the previous example. Um instead of using doubles, I'm going to use short. now I'm again making an array of a 100 million of these uh points, which now are short. um I'm going to do the histogram the

same way. And so, this is the histogram that I got. Um So I've cut it off everything used less memory. Is the array flattened? Or is it an array of objects? Well, if you look back how could we tell the difference? Here. This one here. The array was not flattened. It contained references. And it had 400 million bytes. Here the flattened one. Was bigger. Now we shouldn't

expect all this to be quite as big right? Because we use shorts and not and not doubles. But also over here you saw a whole bunch of objects. Namely. 100 million of them. And here we don't see any objects. Do we see any objects here? Nope. Right? So we have we don't see. Something that would indicate an array We don't see 100 million objects. But there's something

that's troubling. The short is two bytes. Two shorts are four bytes. So if we have 100 million of those four bytes. Shouldn't we see? 400 million instead of 800 million? No, no, it doesn't depend on the so there's a good reason. And it's a puzzler so I. No? I mean, it wouldn't just to initialize. I mean, it it it initializes it with what? You can see what

it initializes it with, namely with these guys here. There is something after each short, but why? No, it's not an alignment thing. So, it would actually pack them tightly. But it I mean, I I understand what you're saying, so that's not a it's not a bad idea not a bad idea. Well, if you look at very carefully here, it's a puzzler as I did something mean. You

can see it is an uppercase short, so it could be null. So, what you see is the nullness bits. And it doesn't just store one bit, right? But for alignment, as one of you said, um it's it's uh it's uh takes you whatever the next reasonable number is. So, you can really see what it does here, right? That it does try to come up with a decent

layout. Um it does the best that it can given the circumstances. If I were to change this uppercase short to lowercase short, then it would actually be half the size. And you can try that out. So, what if I change it to integer, W- what [clears throat] will happen? So, the spec isn't doesn't actually say anything about this, and that's why it's important that uh we have

some tools to observe the behavior. So, what I was trying to do in this presentation is to give you some of those tools so that, you know, when you and your colleagues talk about is is this something that we're at all interested in, that you can start, you know, looking at your workload and actually peek inside and do some measurements. So, and none of these tools are

any beyond the abilities of, >> Java programmer. Um, they're not something that I must say I have used a lot before embarking on this. Um, but they are definitely uh, worth mastering. All right, so um, I in in if you'd want to download this, I give you a bunch of links to um, some Jeps and some videos that you might want to look at that give you

more of the theory because like I say, I've not focused so much on the theory and more on how you can do how you can do the observing. So, where are we? So, it's now been what, 12 years? And there was hope at one point that Java 27 might actually have um, in in the regular version, not in a special build, um, the the first part of

Valhalla, meaning just the value keywords. No no bang yet, but just the value keyword. And of course then the flat allocations. But it was not to be. Um, and I'm not totally sure whether like gremlins in test cases that they didn't want to deal with or um, anyway, um, and now, you know, maybe it'll be in 28 or 29, but as of course it would then be

as a preview feature. So, it would then would still take at least another full cycle to become a a regular feature. Um, so, I can't really tell you whether it's going to be 2 years or 4 years or whatever, but uh, it will happen. >> then but then the story will not end. At some point, there is a dream that lowercase int should be the same thing

as uppercase Integer bang. And there are there's a Jep out to uh, to make that so. at that point the overloading rules will become so complex that no one will understand overloading anymore, but we already don't understand overloading and uh [snorts] so it is what it is. Generics, that is uh definitely the next big construction zone. What we ultimately, of course, want is that uh we can

have a list. We want a list of because, you know, value class that is classes. And then if lowercase int is the same as an uppercase integer bang, you know, we want at some point we want to make sure that if you make an array of lowercase of int that a it's legal and b that it's backed by a flat array, right? That way you don't have

right now you don't want to make an array list of integer because, unless the thing is tiny, um you're worried about the cost. Um but that at some point should go away. That I would say is at least another 10 years away before uh we get there. So, when when they do come, you know, go ahead and benchmark. You've now learned in how to to do some

of that observability. You just use the regular old tools that Java offers you, flight recorder, JCommand, um the the JOL. Um write benchmarks. Uh it's easy enough to do and it's a useful skill anyway. Um it is also, I think, a a good and useful skill and uh to learn more about what the JIT actually does and how to read some of the generated assembly code. Um

of course, it is uh it that takes uh takes a bit more, but it's something that you might want to at some point develop. So, we have a couple of minutes for questions, if there are any. Oh, yeah, yeah, yeah, yeah. Um so, I I so, uh François is going to give a talk tomorrow when? At 1:00 about how to write JMH benchmarks. And so you should

attend. It's a good talk. Yeah. Oh, that brings me to one more piece of advertisement. So I have another talk tomorrow um on uh on stream performance, uh which is more a bit more of a deep dive than today. It's got rescheduled so that there's no conflict uh with Venkat's uh talk at 10:00 tomorrow that I also want to go to. Um and so if you enjoy

this kind of stuff and want to have a bit more hands-on a bit more deep divey than that, then bring your laptop and see me at 11:00. Any other questions? >> Uh I have one question. I'm just wondering you say that uh the wrapper types will eventually uh integer bank something. >> Yes. So >> And I I have one >> Yep. >> thing that bugs me and

I'm wondering how this is going to be solved. That's because I will want inlining. But we also have caching of integers when they're small. And how will this work? So the equality returning true in one case but in the other not. >> All right. So so the the the wrapper classes will go away. And this has been going This has been been warnings for many years now

that they and some other value classes you you're no longer supposed to you compare them with equal equal. You're no longer supposed to synchronize on them. Any program that does that still does that after years of warning that you're not supposed to do that will become invalid at some point. Yeah. So if if for whatever reason you're using an integer of to synchronize on, don't do that

anymore. Um you will get warnings already in uh by by today's compiler. Um Oh, that's a good question. So so yes. So So in with integer bang, you can use equal equal. And so currently the the discussion though is that you should never use equal equal. You should always use equals for consistency. Because it's it's somehow it's weird to to sometimes use equal equal and sometimes use

equals. eventually, there may be operator overloading or something, but that's uh or not. So we don't know. Yeah, but uh the current thinking is with value types, you should still in your programs use equals. I I know. I know. Yes. Yeah, that 30 years ago that whole thing might not have been the best way of setting it up. All right, if you have more questions, just catch

me afterwards. Thank you very much.

From event

jPrime 2026

03 Jun 2026 – 04 Jun 2026

All event videos
Back to Watch