Join the discussion
Write your take first — we'll ask for email only when you're ready to publish.
- Hacker News
- JVM escape analysis has always been hit or miss. Nice to see it getting more predictable.by pan_lid
- I think, in general, one should have the view that anything that can miss, will miss. Agreed that it is better to have something with a lower miss rate.by taeric
- Of course they do, regardless of the language. That is why we have things such as string interning for instance. :)by aatd86
- Escape analysis doing the heavy lifting here. Until it's fully reliable you're still guessing at allocation.by ferrule
- I'd say it's the other way around than escape analysis. The method ABI can always scalarize value class types without escape analysis, but if they ever need to get type-erased or written atomically you end up with extra allocations for what would have been a single object before. Similar to boxing value types in C# really.by debugnik
- I’m not sure I fully understand why the compiler can’t derive the specialized apply method in this case.
I guess I follow the technical reason that the interface doesn’t override the method so there can’t be a specialized version of it. But that’s only visible through reflection right? Not in the direct language semantics?
by dullcrisp - Hi, the author of the blog post here.
I think that your misunderstanding comes from the fact that you base your understanding in Java semantics, but we really need to deal with the JVM's semantics. Your suggestion only makes sense when:
a) All of the typing information is preserved across a whole build b) The build is entirely static, no new classes can be loaded
Both of these are at odds with how the JVM functions. First: We lose type parameters on compilation, so we only see that Frobber extends Fun, the type parameters are lost and its usages are converted to Object.
Second: The JVM loads code by lazily loading classfiles. All classfiles are independent, and more can be added to a system.
- Since being created, the Java pitch was "don't worry about low-level stuff like value/reference classes, a Sufficiently Smart Compiler will automatically pick the best option given your code and runtime profiling".
What changed, why suddenly they adopt C++ features they explicitly excluded?
by dist-epoch - I don't think that was ever the case...value(primitive) types were there from day 1 and the given reason that it is too much overhead to use objectsby pestatije
- On the other hand, value semantics are just plain easier to reason about.
My 2 is your 2. We don't have different 2s because it makes no sense.
But sometimes you don't just want a 2, you want a pair (2,3). All of a sudden, my (2,3) isn't your (2,3), unless you call equals instead of ==, and remember to implement equals() (and that demands implementing .hashcode() too, and probably a toString() while you're at it). While it's easy to forget about if you've been using Java for a while, 2 gets passed by value, but your pair gets passed by pointer-value.
So I'd come at it from the other side. Pick a value representation of (2,3), reap the simplicity, and hope the sufficiently smart compiler is smart enough to do good things with it.
by mrkeen - It was not at all “suddenly”. The discussions about explicitly defining value types because escape analysis is insufficient have been going on for at least a decade.by mcculley
- This was pretty much always wrong. Nobody writing performance critical code in Java trusts the compiler to magically figure things out, and the sort of code you arrive at if you want Java to go fast is generally unidiomatic.
- First, it's not a C++ feature. In C++ you tell the compiler how to lay out objects in memory. Here you declare what properties your class has (e.g. whether it needs identity or not), and the compiler decides how to lay out each of its instances in memory, and may automatically do it in different ways in different places. So the principle that "you tell us what semantics you want and let the compiler figure out the implementation" remains in effect.
Second, the reason why the compiler cannot infer on its own that a class does or does not need identity without you declaring it is that the use of identity can be in a different module.
by pron - Even a smart compiler can't break the program semantics, and without a closed world assumption it simply can't assume that an entirely different part of the program doesn't expect to observe object identity for a type.
Java is adding small, orthogonal features that amount to the same feature set as value types in other languages, but can be cherry-picked into existing code for partial advantages without significant changes. These value classes are still nullable, lack a guaranteed layout, and can't be observed torn, unlike in C++/C#/Go.
by debugnik - Java is so dynamic that it's impossible to prove a class will only be used in "value friendly" ways. When objects have no identity the meaning of `==` is different, and the compiler would have to do some kind of whole program analysis to find out if there is any way an instance of the class could ever be checked for equality.
That's hard when you have type erasure and polymorphism and runtime class loading. And even if they pulled it off it would blow up compile times and be programmer unfriendly because adding one line could deoptimize an important class defined in another package. So they decided to bite the bullet and add a way to statically opt in for faster but incompatible behavior.
by tancop - >What changed, why suddenly they adopt C++ features they explicitly excluded?
Project Valhalla, which includes the effort to add value types was announced in 2014. They've been working on it for a while.
As for "what changed" ...
Back in 1990s when Java was conceived, there was an idea that CPUs in desktops had plenty of extra cycles that were being wasted and could therefore be used to reduce mental load on developers. It was the same "cpus are cheaper than developers" idea that is repeated today with "tokens/cpu are cheaper than developers". With that philosophy, James Gosling talked about Java's "everything-is-an-object" as a mental simplification for developers. All the extra indirections of pointer-chasing to box unbox primitives and/or iterate through arrays of objects wasn't seen as a penalty (again, "CPUs are cheap; devs are expensive").
However, the later evolution in 2000s of CPU hardware vs RAM hardware changed that performance tradeoff thesis: https://en.wikipedia.org/wiki/Random-access_memory#Memory_wa...
Now having value types that are contiguous in RAM is a big deal for performance. Avoid a bunch of pointer chasing. Even C++ best practices were affected. E.g. the traditional tradeoffs you learned from from classroom textbooks of linked-lists being faster than arrays for middle-of-list insertions was no longer always correct in the new world where CPUs are caching adjacent RAM areas to try to reduce the memory wall issue. So O(n) could be faster than O(log n) depending on the size of the data structure and interactions with RAM pre-fetch, etc.
by jasode - Common Lisp has some value types, specifically numbers and characters. Any implementation is allowed to copy these objects at any time. For this reason the EQ function should not be used on numbers or characters, as the results may be unpredictable. The comparison function EQL should be used normally in its place.
(Integers and characters also are often represented by "immediate" values that reside inside what would otherwise be a pointer; for these EQL and EQ always do the same thing. But bignums and larger floats allocated on the heap can be different.)
It would be interesting to extend Common Lisp to have types that operate similarly. For example, structures that are not compared by object identity but by EQL of their fields. I presume these are the value types being discussed here for Java.
One nice thing about value types is they play well with distributed computing. Serialize/deserialize and the values stay the same; no need to have references to objects sitting off in another computer.
by pfdietz - Good technical overview, and I fully agree with the conclusion's sentiment at the end:
``` Declaring a value class is first and foremost a semantic decision. It tells our fellow programmers that its instances are defined entirely by their state and do not need identity. That clearer model is valuable in itself! The JVM’s additional freedom to optimize how those values are represented is a welcome bonus. ```
Many developers seem to think that "go value go broom", but the truth is much more nuanced and the idea should not be to think about "but performance" but to think about the nature of your underlying data. At the very least this integrates some core DDD lessons directly into language. I'm glad tearing isn't turned on by default exactly for this reason.
Java was always a language that geared itself towards making libraries easy to use, putting much faith in the library author and strong encapsulation. Now, experts can gain significantly more performance from the JVM, while more humble programmers are avoided from creating bugs they will not expect.
by DarkNova6