JDK 28
Posted by ejboy 4 days ago
Comments
Comment by jmarlow47 4 days ago
The last few years of server-side has been on Kotlin for me, however, modern Java is looking more and more like anything I'd need in a language.
Comment by ejboy 4 days ago
Comment by kodebach 4 days ago
Sure, Java is slowly getting most of the features and sometimes even in a better form because they control the runtime (virtual threads arguably just work). But IMO Java is still incredibly verbose, Kotlin's type system is just better and I've genuinely never come across anything that would be easier to do in Java. Even virtual threads can be used from Kotlin.
Comment by ejboy 3 days ago
Comment by za3faran 3 days ago
Comment by kodebach 3 days ago
if (obj instanceof Point(double x, double y)) {
foo(x,y)
}
Kotlin with its smart cast feature lets you do if (obj is Point) {
foo(obj.x, obj.y);
}Comment by ejboy 2 days ago
if (obj instanceof Point p) {
foo(p.x(), p.y());
}Comment by MBCook 4 days ago
I love Java and I’m so glad it’s kept improving so much despite all the turmoil in its ownership.
Comment by pjmlp 4 days ago
Many years ago I learnt that mastering the language that goes alongside the platform is quite valuable versus the languages that come and go.
Hence why despite my rants, I keep my C knowledge up to date, as it is the language of UNIX, same with JavaScript on the Web, Erlang on BEAM, and naturally Java on the JVM, even though Scala and Clojure are much cooler.
Comment by ksec 4 days ago
I don't think that is a fair assessment. Java / JVM has been steadily progressing in the past 10 - 15 years. From languages to different part of JVM. It is only getting the sense development being picked up because HN and other social media are finally getting the clicks and interest again. The pendulum swing back because of all of different reasons.
And in reality a lot of these recent landed improvement were 5 - 10 years in the making.
I still don't like its syntax though. But I am not even sure if it is even an issue in AI driven development era.
Comment by rjrjrjrj 3 days ago
Comment by brianwawok 4 days ago
Comment by jjav 4 days ago
Pretty much the opposite. Those who disliked it usually cited having to type too much due to class verbosity and variable verbosity (the former has merit, the latter was purely a choice).
If you no longer need to type in any of the code, what better than a language with a blazing fast and scalable runtime, with excellent debugging and monitoring support.
Comment by kodebach 4 days ago
Rust has all the individual bits you need as libraries (web server, database access, etc.) but you have to wire it all together yourself. Sure LLMs can do that too, but how do you know the LLM did it correctly? There could be complex interactions between the individual libraries.
Using a complete framework solves that, you know its bits will work together correctly. It also makes the code structure of different applications look more similar, which probably helps LLMs understand it better too.
Comment by pjmlp 4 days ago
Thus anyone without issues using a GC language, regardless of which GC shape, has already plenty of choice on the JVM, not only the frameworks.
Comment by satvikpendem 4 days ago
Comment by za3faran 4 days ago
Comment by satvikpendem 7 hours ago
Comment by jjav 4 days ago
I don't follow Rust as much as I wish I could if I had more free time.
What platforms are supported in Rust that don't support Java?
Does Rust have diagnostics, monitoring and debugging support anywhere near JVM quality?
Comment by satvikpendem 4 days ago
Comment by pjmlp 4 days ago
PTC, Aicas, microEJ, Ricoh, Xerox, Cisco, and several M2M vendors.
The first three even have bare metal variants, where the runtime is the OS.
Naturally they aren't targeting ESP32, Arduino, PIC class of devices.
Comment by satvikpendem 2 days ago
Comment by pjmlp 4 days ago
Most rewrite language X in Y are justifications after the fact, when the real reason is language Y was cool for whatever reason.
Comment by satvikpendem 3 days ago
Comment by wolvesechoes 4 days ago
I know this is a heresy in some online religious groups pretending to care about engineering but that's reality.
Comment by satvikpendem 2 days ago
Comment by bheadmaster 4 days ago
That phrase "complex enough" seems to be quite load bearing.
At which point does complexity reach the tipping point where a Java implementation becomes faster? From my experience, the more complex the application, the more enerprisey the Java code becomes, relying heavily on dynamic capabilities that make it slow even by Java standards.
Comment by pron 4 days ago
Comment by anko 4 days ago
- junit parallelisation - how are shards handled? how do you balance the tasks across forked processes? What is the bottleneck? Options are 1) pay someone for better tooling or 2) write your own
- long pauses in garbage collection - how do you know which objects are causing the pauses? the options; 1) profiling 2) analysing heap dumps that you periodically capture 3) add metrics or gc logging
- what is the performance bottleneck? options 1) profiling
Sure AI can do this stuff, but it's painful and time consuming
Contrast it to Elixir on the BEAM;
- exunit parallelization - basically free and because you don't have coupling due to OOP. mocks are basically an antipattern
- long pauses due to gc? They just don't happen - everything is isolated and can be cleaned up in parallel.
- performance bottleneck? Open inspector and see what is doing all the reductions, and if any of the inbox queues are backlogged. You don't have to profile, you can just look in production - memory? Open inspector and sort processes by size descending. Again no profiling needed
Comment by pron 4 days ago
> Contrast it to Elixir on the BEAM;
Java is in the same performance class as C++. BEAM is wonderful in its own way, but it's not even in the same performance class as Go.
BEAM was designed for some interesting resilience in distributed systems. The JVM was designed to address the performance issues that programs in C++ and other low-level languages suffer from when they get large and/or are maintained/evolved for a long time.
> long pauses due to gc? They just don't happen - everything is isolated and can be cleaned up in parallel.
GC pauses in the JVM have been optional for a few years now. ZGC doesn't have GC pauses.
Comment by anko 4 days ago
The parent was saying these new features don't matter as much post-AI and i think I agree with them. The AI can just write in rust if you need to optimise a hot loop, but if you need observability, you could do worse than the BEAM.
> GC pauses in the JVM have been optional for a few years now. ZGC doesn't have GC pauses.
yeah that's a fair comment, zgc is a lot better for pauses. I'd still say that it pulls along a fair bit of incidental complexity - it encourages you to allocate a fair amount of memory to your heap, which isn't ideal in this world of kubernetes and microservices. If you leave on uncommit (returning memory to he OS periodically) you get extra latency penalties which are just as painful as garbage collection stalls IMHO.
Comment by pron 3 days ago
> The AI can just write in rust if you need to optimise a hot loop
Why would it when it could just write it in Java? The JVM was invented not because we couldn't write code in C++ but, among other things, to optimise things (especially around memory management and inlining) that low-level languages have trouble optimising in a large class of applications. If you're after the best performance, then you'll ask AI to use a low-level language when a low-level language offers better performance, and to use Java for applications when Java offers better performance.
Java isn't a performance compromise. It offers different tradeoffs that could make some programs significantly slower than low-level languages in some cases and significantly faster than low-level languages in others. That's the point of the approach taken by the JVM: to offer optimisation opportunities that aren't available in low-level languages. People who think low-level languages always offer better performance are people who have not had much experience with low-level languages. I originally came to Java from C++ because Java has optimisation opportunities that low-level languages don't (they have to compromise on performance in some situations for the sake of low-level control) that matter in certain but very common kinds of programs.
> zgc is a lot better for pauses
It has no real GC pauses. It does have minuscule pauses for synchronisations, but those are an optimisation, and they're no worse than OS jitter. In other words, if ZGC's pauses are a problem for you, then you won't use a non-realtime kernel, as you'll get similar pauses even in a C program on the same OS.
> I'd still say that it pulls along a fair bit of incidental complexity - it encourages you to allocate a fair amount of memory to your heap, which isn't ideal in this world of kubernetes and microservices.
It really doesn't. All of our GCs are optimised for a natural rate of allocation. They do discourage pooling objects to artificially reduce allocation rates, though, but it's easier to not pool than to pool.
> If you leave on uncommit (returning memory to he OS periodically) you get extra latency penalties which are just as painful as garbage collection stalls IMHO.
You get such stalls and worse with C++ or any other low-level language.
Comment by pebal 3 days ago
Comment by pron 3 days ago
BTW, SGCL is a non-moving mark-and-sweep collector and non-moving collectors are no more similar to moving collectors than malloc/free is similar to moving collectors. Garbage collection contains different classes of algorithms with different goals that are so different from one another that it's almost pointless to put them in the same category. Java uses moving collectors because it's the class of algorithms designed to reduce the throughput overhead of all non-moving approaches (from manual malloc/free, through automatic RAII and refcounting GC, through tracing mark-and-sweep collectors like SGCL or Go's GC). In important ways, all non-moving approaches are more similar to each other (regardless of the use of a GC) than any of them are to moving approaches. (Java did have a concurrent mark-and-sweep collector at one time, but we removed it some years ago)
Comment by pebal 3 days ago
Comment by pebal 3 days ago
Comment by tofflos 4 days ago
542: PEM Encodings of Cryptographic Objects
543: Structured Concurrency
Developers work with certificates a bit too seldom to aquire proficiency with conversions. Being able to use PEM directly will ease a lot of pain.I've given concurrency a couple of attempts over the years and I don't... like... or understand?... how the existing Java concurrency primitives are supposed to work. The only library that has clicked with me is Awaitility but for some reason Awaitility is intended for testing and not for production. Maybe Structured Concurrency will finally unlock something for me?
Comment by skissane 3 days ago
While I certainly welcome this in the JDK, in practice it hasn't been a huge issue since a lot of people just use Bouncycastle to get that support.
Comment by rob-olmos 3 days ago
Comment by hackingonempty 4 days ago
Comment by amelius 4 days ago
Comment by twic 4 days ago
$ echo 'class Hello { public static void main(String[] args) { System.out.println("Hello, world"); } }' >Hello.java
$ javac Hello.java
$ java --version
openjdk 21.0.7 2025-04-15 LTS
OpenJDK Runtime Environment Zulu21.42+21-CRaC-CA (build 21.0.7+6-LTS)
OpenJDK 64-Bit Server VM Zulu21.42+21-CRaC-CA (build 21.0.7+6-LTS, mixed mode, sharing)
$ time java -cp . Hello
Hello, world
real 0m0.026s
user 0m0.010s
sys 0m0.018s
For some reason this machine is still on 21. Anyway, the overhead of the JVM itself is pretty low.For small tools it's fine:
$ echo 'class Dig { public static void main(String[] args) throws Exception { var addr = java.net.InetAddress.getByName(args[0]); System.out.println(addr); } }' >Dig.java
$ javac Dig.java
$ time java -cp . Dig news.ycombinator.com
news.ycombinator.com/209.216.230.207
real 0m0.047s
user 0m0.026s
sys 0m0.021s
$ time host news.ycombinator.com
news.ycombinator.com has address 209.216.230.207
news.ycombinator.com has IPv6 address 2606:7100:1:67::26
real 0m0.052s
user 0m0.005s
sys 0m0.004s
Java startup gets slow when you use some massive framework like Spring. If you need a program to start fast, don't use one!Comment by kodebach 4 days ago
Or you can just use a different framework like Micronaut which does none of that and uses an annotation processor at compile time instead. (Similar to Spring AOT, with fewer limitations)
Comment by MiroslavPokorny 3 days ago
Comment by tofflos 4 days ago
void main() {
IO.println("Hello world");
}
$ time java Hello.java
Hello world
real 0m0.662s
user 0m2.421s
sys 0m0.198s
void main(String[] args) throws Exception {
IO.println(InetAddress.getByName(args[0]));
}
$ time java Dig.java news.ycombinator.com
news.ycombinator.com/209.216.230.207
real 0m1.154s
user 0m2.379s
sys 0m0.222sComment by elFarto 3 days ago
#!/usr/bin/java --source 25
void main(String[] args) throws Exception {
var addr = java.net.InetAddress.getByName(args[0]);
System.out.println(addr);
}Comment by ww520 4 days ago
Comment by marginalia_nu 4 days ago
Performance depends a lot on how you write the code. Inefficient Java code is often quite slow, but you can get surprisingly close to C++ speeds with Java in many cases, but that involves a lot of byzantine rules of how to write code so that it e.g. omits bounds checks. More realistically if you don't pay any attention you're probably running at like a 25-50% speed penalty.
Java has a leg up on statically compiled languages in that it does automatic PGO based on real run-time data which helps bridge the gap to a surprising degree. GC is also a lot more resistant to memory fragmentation from repeated small allocations than malloc, which can help as well, not just from its generational approach but also e.g. TLAB space.
It's a bit held back by not having decent access to SIMD intrinsics. Maybe will resolve itself when the Vector API gets finished baking eventually.
Comment by binary132 4 days ago
- AOT compilation: https://openjdk.org/jeps/544 - ~100ms startup for ZGC: https://openjdk.org/jeps/545
Comment by pjmlp 4 days ago
Comment by jiggawatts 4 days ago
Almost all Java benchmarks are for the "steady state" after half an hour of warm up time.
Comment by invalidname 4 days ago
There are other solutions that give you MUCH faster startup with lower memory e.g.: https://foojay.io/today/java-server-work-before-startup/
Edit: it seems I missed that 28 added the actual native code compilation phase so it will probably be closer to the GraalVM numbers in the link above.
Comment by pjmlp 4 days ago
OpenJDK, OpenJ9, Azul, PTC, Aicas, microEJ, ART (even if not Java TM), GraalVM, Bluesoft,..
Comment by kg 4 days ago
Comment by miroljub 4 days ago
Comment by samus 4 days ago
Comment by pjmlp 4 days ago
Comment by samus 1 day ago
Hotspot's AOT is promising and doesn't have those pitfalls, but has not yet been delivered.
Comment by azatom 4 days ago
Comment by OrangeDelonge 4 days ago
Comment by za3faran 4 days ago
Comment by OrangeDelonge 4 days ago
Comment by illusive4080 4 days ago
Comment by cogman10 3 days ago
So long as you are willing to do a training run, you can get pretty decent startup times.
Comment by pjmlp 4 days ago
Comment by mihular 4 days ago
Comment by pjmlp 4 days ago
Also performance for what?
As long production SLA are met, that's a deployment success.
Comment by AlotOfReading 3 days ago
For example, the C++ replacement for a Java program bottlenecking my CI takes 1/3rd as long (with less variance) and runs on a smaller instance.
Comment by pjmlp 3 days ago
You missed to account the hours spent writing the application in C++, related memory and UB bugs, the additional security issues plugging a language like C++ directly into the Internet.
Additionally, you also failed to account Java has JNI, which means only a few hot-spots might be rewriten into something like C++, not the whole application.
Finally, with Panama, it is possible to design a memory layout off heap, similar to what would be done with C++.
Naturally this requires some low level programming skills, the benefit is that one keeps enjoying the ecosystem.
Finally, SLAs also include execution time, of course.
Comment by fniephaus 4 days ago
Comment by __natty__ 4 days ago
Comment by alfiedotwtf 4 days ago
Comment by papercrane 4 days ago
There is also JavaFX, but that is no longer bundled with the JDK.
Comment by fian 4 days ago
Comment by TeaVMFan 4 days ago
* React replacement: Flavour ( https://frequal.com/Flavour/book.html )
* Vanilla JS equivalent: TeaVM ( https://www.teavm.org/ )
Comment by fithisux 4 days ago
Comment by pjmlp 4 days ago
Comment by happyPersonR 3 days ago
Comment by MrBuddyCasino 4 days ago
> trailing commas and comments are not supported
Be strict in what you produce, be lenient in what you accept.
Grr.Comment by chowells 4 days ago
Comment by MrBuddyCasino 4 days ago
One is „accept a wide array of syntactically malformed input, ill defined, substantially complicating the parser“, the other „accept two trivial extensions to the standard that are widely used and don’t affect the resulting data in any way.“
Comment by dwaite 4 days ago
Comments were explicitly disallowed from JSON since the beginning, because they could be used as a side-channel for vendor-specific extension mechanisms and harm interoperability.
Comment by dwohnitmok 4 days ago
That's always how it starts. If they're truly trivial the reasoning also cuts the other way. Remove them upstream.
Comment by mpyne 4 days ago
In that case they made the right choice, as machines won't be inserting trailing commas or comments in the first place, and the risk of having consuming apps inadvertently accept invalid data would be high otherwise.
Comment by pixl97 4 days ago
Comment by mpyne 3 days ago
But from the perspective of a potentially distributed ecosystem of apps and libraries as employed in practice in modern systems, it's actually harmful to interoperability.
Other libs/services/apps that touch the malformed JSON that this change would accept might be present in the overall system and crash on that data. The risk grows with the more components of the overall system that silently accept malformed data and allow the bad data to make its way deeper before it causes issues.
This is what “fail fast” was meant for, if something is going to cause problems in your system at some point in the future, make that “some point” happen ASAP, so that it is easier to identify and fix the source of the flaw.
Comment by MiroslavPokorny 3 days ago
Comment by vrighter 4 days ago