Unless you want serious long term pain then you shouldn’t be more than one LTS release behind. So if you are not running on JDK 11 or later you should be strongly thinking about upgrading.
Unless you want serious long term pain then you shouldn’t be more than one LTS release behind. So if you are not running on JDK 11 or later you should be strongly thinking about upgrading.
My point is that migrating to new releases isn't always trivial, especially the more complicated and complex a project gets. If i knew that i'll run if compiled successfully, then it wouldn't be too bad, but with the amount of reflection, dynamic class loading etc. that frameworks like Spring favor, my workflow to date has been fixing a bug, building and running, something else breaking, fixing that bug, building and running, something else breaking, realizing that i cannot fix this because upgrades to the logic would be inherently "lossy" due to a mismatch of what the new framework versions provide, making it so that breakages that aren't covered by tests will also be created and so on ad infinitum.
Sometimes it makes me wonder why JDK 9 onward just didn't have a compatibility module: "Here, install this to have all of the old Java classes that were removed from the standard library after JDK 8 and retain that old functionality for software projects that would otherwise be stuck in development hell short of a full rewrite." Or something like that for Spring Boot, that lets you use web.xml instead of having to use hacks to load its contents and register all of the servlets, with half of them not working anyways, because some class names were changed along the way.
Software doesn't always age beautifully.
Furthermore, it feels like new runtime and package versions are made in ways that accidentally break backwards compatibility with no clear ways to avoid this.
Mind you; you usually end up with the services all in different states of node/jdk/etc versioning(at least the companies I worked at).
Here's a list of the things that were available in JDK 8 and no longer are: https://advancedweb.hu/a-categorized-list-of-all-java-and-jv...
When you stumble upon something like that, at best you can import a Maven dependency with a version that has what you need, at worst you need to rewrite the code to use another library or set of libraries.
If you have any low level logic like custom class loaders written against older JDK versions (think before 8), then they'll be forwards compatible until 8 for the most part, but will break afterwards. Coincidentally, reading code that deals with low level logic is also not easy to do, especially if it's not commented well.
If you rely upon reflection, or use advanced language features (like the JasperReports framework for generating PDFs, which also has a build step for building the reports), in some cases things might compile but not work at runtime due to class mismatches.
Many frameworks need new major versions to support newer releases than JDK 8, for example Spring Boot 1.5 needs to be upgraded, so you're also dealing with all the changes that are encapsulated by your dependencies. In another project that i also migrated, needed to rewrite a lot of web initialization code for Spring Boot 2.X.
Not only that, but with those framework changes, certain things can break in the actual environments. For example, if you package your app as a far .jar, then you'll no longer be able to serve JSP files out of it. It makes no sense, but packaging it as a .war which can be executed with "java -jar your-app.war" will work for some reason.
I some other libraries, method names remain the same, but signatures change, or sometimes things just get deprecated and removed - you have to deal with all of that, which is especially unfun in code that isn't commented but that the business depends on to work correctly. Throw in external factors such as insufficient coverage of tests and you're in for an interesting time. I'm not saying that it's a type of environment that should be condoned, but it's the objective reality in many software projects.
Oh and i hope that you're also okay with updating all of your app servers (like Tomcat) or JDK installs on the server as well, especially if you depend on some of the Tomcat libraries to be provided and for your frameworks/libraries that depend on them being present at runtime to accept those new versions effortlessly. It all feels very brittle at times.
This is especially a nightmare if your servers were configured manually - personally i'm introducing Ansible and containers to at least isolate the damage of Ops rot, but it's been an uphill battle since day 1.
Here's an exceedingly stupid one: sometimes there are checks in code to make sure that you're using the right version of JDK (or even the Oracle JDK), which get confused with the larger versions. It's easy to fix when it's your code, but really annoying when it's external tools - personal nitpick.
Addendum: here's something that i expected to break, but didn't. We use myBatis as an ORM. It has XML mapper files that define how to map entities and construct queries against the DB based on that. Also, it uses Java interfaces and dynamically calls the underlying XML code as necessary. So essentially you have an interface, which has a corresponding XML file in which you have quasi-Java code (e.g. checking what parameters are passed in to the query) that's used alongside a number of tags to dynamically build SQL queries. Here's an example: https://mybatis.org/mybatis-3/dynamic-sql.html
Instead of something breaking in myBatis, what broke actually was Hibernate in the newer versions of Spring. Oh, and Flyway for DB migrations simply used to work with a particular Oracle DB version as well.
This silently broke a bunch of scripts and callbacks because of class cast exceptions and such. The solution was to rewrite the scripts in such a way to import the Java Long class and use that directly to express large integers.
The most important breaking issues I've found were not necessarily in our code base, but in dependencies. Libraries that include libraries that include libraries, all doing their own thing, a lot of the time involving some pretty scary reflection stuff for some unexplained reason. Blindly upgrading libraries only gets you so far, sometimes a dependency gets abandoned and you need to find a replacement and rewrite your code to call the APIs in the right manner to be backwards compatible enough.
This is where it always seems to end up. Why do people bend over so far backwards doing reflection that isn't specified?
Either because there's no other way to do it, or because the official way is too slow.
(I always found this really frustrating when making the argument for Scala. Yes, Scala-the-language is more complex than Java-the-language. But it's not more complex than the pile of reflection magic that anyone who tries to write a nontrivial system in Java actually ends up using).
All that said - for the 'normal' Springboot applications, we saw almost a 25% performance jump for the same code - updated libraries - running on K8. Java 11 was a substantial performance improvement. The jump for us to Java 17 will happen this winter, and looks to be a non-issue so far.
In some ways, this is just a consequence of a kind of evolutionary leap forward for many applications. We don’t deploy our WARs into application servers anymore, we try to pack a fat JAR, or maybe even a statically linked runtime, into a container. We rebuild the whole world on CI all the time. I mean, Java 8 has issues just respecting cgroup limits, which many a despondent ops engineer has discovered.
I think there’s still room for the 1M SLoC monolith even in this new world, but there are real benefits on the horizon for upgrading to Java 17 and beyond. We’ve reached a point where it’s just more expensive not to make upgrades a regular part of you development cycle. And, no one is stopping you from just staying on 1.8. I mean, I’m sure there are still decaying enterprises stranded on 1.3.
Sadly, i don't get that choice, nor do i get the choice to make the judgement call to leave the monolith on JDK 8 in the name of stability. But hey, at least i'm paid a bit of money for it, so i have some motivation to work on all of it to the best of my abilities, learn a bit more about the JDK internals, have a word or two to tell others about my experience before i inevitably burn out from the churn.
Personally, i do still think that Java is pretty good when it comes to stability and backwards and forwards compatibility, especially since tooling like Maven is actually pretty good when compared to the alternatives (well, the parent POM functionality is confusing sometimes, but oh well, it has its use cases). That said, JDK 8 to newer releases was indeed a generational shift (probably one for the better) and you see similar things with Spring Boot 1.5 to Spring Boot 2.X, those breaking releases are inevitable.
I only wish that the things blocking people from writing more modular systems would disappear over time, so that huge monoliths that are incredibly hard to work with wouldn't be such an issue. I'm not necessarily advocating for microservices here, since people tend to go straight from one ditch into the opposite one (multiple services per person, nightmarish service mesh, needlessly large % of the code being for shuffling data around, suddenly building a distributed system even within a single domain), but at the very least it would be really nice to have someone look at it and say: "Hey, this PDF generation logic looks really brittle and perhaps should be a service of its own." or maybe: "Oh, hey, we're serving our RESTful API calls and front end resources from the same application, maybe we should have a separate front end app, served through a regular web server?"
Then again, if nothing else, i always have the choice to choose where i'm employed, even though i don't feel like letting my coworkers down at the moment either.
I'm in the middle of a migration to 11 and my general approach has been to get it compiling on 11 while targeting 8 with (--release 8). That seems to catch a great many things that would otherwise fail. The hardest part thus far was figuring out the appropriate libs to replace Java 8 ee stuff, mostly wrt soap and xml. I'm hoping moving to 11 goes smoothly but we'll see. I'm hoping to have the artifacts to a point where they can run on either 8 or 11 without issues.
I don't know PrimeFaces, but I have worked with JSP, which does have a flag to set the compiler version (in case you were ever thinking about putting java lambda's in JSP files, which is possible but not recommended due to mixing code).
The hardest part of upgrading the JDK is going from 8 to 9. After 9 it is much more forgivable. As for Spring, major versions are a pain, as is Hibernate. The other parts of Spring are generally well upgradable if you read the Spring changelogs.
They are in there but easy to miss - Bellsoft actually have builds for both jdks and jres for more than mainstream x86_64. Termurin dont even have jres anymore (last time I checked)
Nowadays no one keeps just one JRE in their laptop/desktop and disk sizes are in TBs so no point in saving 100 MB and lacking development tools.
And in one targets containers or systems with small amount of disk jlink give more gains than JRE.
Java has been "breaking changes" between releases to make python blush for a few years now, 17 finally looks like a worthy migration from 8.
323M jdk
136M jre
Still, not 400 MB difference.If you want jdk you don't use jlink.
didn't say it was? A java.base jre is about 40mb extracted jdks extract to about 400mb (depending on os/arch)
A downloaded then extracted jdk will take over 500mb of disk space
Upgrades now are pretty straightforward if you are past JKD 9 - with JDK 16-17 being a bit tricky, but less than JDK 9.
Source?
In other words, OpenJDK does not follow any LTS/non-LTS release cycle. Hence there is nothing special about Java 17 vs 16 when it comes to stability.
Using so called LTS without paying for it is in no way different from using latest. You get similarly tested software and no one to call if you need a security fix asap.
That depends. If most people follow the advice of "sticking to LTS", these versions will have way more users, and thus will be more "battle-tested", as in tested in production.
There haven't been any notable language features added since java 9 besides some basic syntax sugar (which is already covered by stuff like lombok anyway).
For features and performance you might as well just target .NET 6. It has things that have been perpetually 'too hard to implement' (read: oracle doesn't want to pay their engineers to impl it and will sue you if you do it yourself) like value types, generics without type erasure, no checked exceptions, etc. and with .NET 6 performance is better than OpenJDK across the board.
.NET 6 is vastly more performant than .NET 5, which was already faster than openjdk and openj9 (https://devblogs.microsoft.com/dotnet/performance-improvemen...)
> and also what do you mean by oracle suing if you implement value types?
this one shouldn't need an explanation, oracle very commonly pursues frivolous lawsuits as a way of bullying money out of businesses that don't have the budget to fight them for years in various courts.
I do find people comparing Java and .NET Core often are compare apples to oranges however. Working on both languages it is just my opinion but the .NET platform is newer - it has a better "base" even without the same man hours. Much of the engineering time in both ecosystems is spent optimising for code typical to that ecosystem which is affected by history/legacy like any other software system.
The previous versions in this stream (.NET Core 2.1, .NET Core 3.0 and 3.1) also run fine on Linux. For web server / API server workloads, it's been stable for a lot longer than 11 months, and people do indeed "bet the farm" or "run the business" on it, quite happily.
Technically, this starts with .NET core 2.1 on 2018-05-30. The prior version, 2.0, released on 2017-08-14, wasn't _quite_ there yet although some people were happy with it for production use.
.NET Core 2.0 (August 2017) we assume.
Have you even seen the benchmarks compared to Java and other languages?
https://www.techempower.com/benchmarks/#section=data-r20&hw=...
If you're not paying attention to .NET its time to get your head out of the sand, seriously.
"no real reason" is a stupid take. I could list multiple, but a big one is that there are hundred java developers for each dotnet developer in my city. The java env is well tested and well understood, if anything one should argue why not use it instead of a hip and trendy alternative.
In what way? It's slower and has less features than the .NET runtime.
> "no real reason" is a stupid take. I could list multiple, but a big one is that there are hundred java developers for each dotnet developer in my city. The java env is well tested and well understood, if anything one should argue why not use it instead of a hip and trendy alternative.
So, your only credible excuse for using java is inertia from boomers and middle managers refusing to adapt from the standard of the early and mid 2000s?
If you come in and freely migrate all of the Java codebases to .NET while maintaining code quality and functionalities, I'm sure many people would let you do it. If you don't understand why people stick to one language, that means that you've never worked on a big codebase, or completly ignore the business side of the developer job. In both cases, that's a lack of wisdom on your part.
Perhaps not solved, but at least partially addressed with value classes[1] and reified type parameters[2].
[1] https://kotlinlang.org/docs/inline-classes.html
[2] https://kotlinlang.org/docs/inline-functions.html#reified-ty...
pretty odd take, in which country?
Lots of people use .NET in enterprise, I don't know where you got that impression.
Regardless, i actually compared Java with .NET and their web frameworks as a part of my bachelors', everything from synthetic benchmarks for encryption and data processing and transformations, to things like shuffling JSON around. Now, it's all in Latvian and was a number of years ago, so it's not entirely relevant at this point, but i did have some tangible findings.
In short:
- both Java and .NET (then Core) are inconsistent in their performance - there are certain things which are slower in one technology than other by not using external optimized libraries. For example, Java had problems with writing deeply nested dynamically generated JSON with JavaEE libraries (now Jakarta), whereas .NET Core had problems with handling large amounts of text
- their performance was largely comparable in most other tests, neither was faster by a factor of 10, like you'd see with Python and Ruby compared to either
- thus, it's largely a matter of choosing the actual frameworks that you'll want to utilize properly and consider both the job market and business factors (familiarity with the tech stack, job market etc.)
- in summary, they're close enough for it to not be a technical decision most of the time in real world circumstances (save for a few exceptions), but rather is a decision that depends on social elements
Since then: - i don't believe that the observation of them being "close enough" has changed much, both in legacy code and otherwise
- .NET Core and now .NET 6 has improved bunches with its runtime; Core was so successful it's essentially the future of the platform (i feel bad for developers who'll be tricked into working on legacy code with the old .NET and IIS, versus the new one and Kestrel)
- JDK has improved bunches with its runtime and GC; the runtime situation is a bit complicated and cumbersome, considering the OP's article, but overall it's pretty usable, especially with frameworks like Quarkus
If you care about benchmarks and vaguely realistic performance comparisons in the current year, simply have a look at the TechEmpower benchmarks: https://www.techempower.com/benchmarks/#section=data-r20&hw=...If you jump around the different tabs that compare which frameworks do what better, on average:
- .NET is better for plain text
- .NET is noticeably better for data updates
- Java is noticeably better for JSON serialization
- Java is noticeably better for single DB queries
- Java is noticeably better for multiple DB queries
- as for cached queries and other use cases, there's more variance
- neither is better than the other for it to matter a lot
These are probably better than me linking the bachelors' because it's done in a better controlled environment, with more resources, and a lot of people contributing to the source code of the benchmarks: https://github.com/TechEmpower/FrameworkBenchmarks/tree/mast...In short, you're not necessarily wrong - there have indeed been great improvements to the .NET platform and it's good to see it finally being a capable and performant tech stack that you can use on *nix. But you're also not right: Java is getting similar attention, even though it's still lagging behind a few years in regards to "having its stuff together" (e.g. going from Oracle JDK to a more stable model + the JDK 8 to newer version shift, which is similarly painful with old .NET versions).
To lighten the mood, i'd consider suggesting that anyone also have a look at Go, which is similarly performant and allows you to build runtime independent executables by default, which is easier than in either .NET or Java. It's also pretty easy to write and has seen good use for the development of tools, since its startup time is a tad better than either that of Java or .NET as well. Here's the three compared: https://www.techempower.com/benchmarks/#section=data-r20&hw=...
All of this is currently being implemented in project Valhalla...
It's comparing apples to oranges - in this case .NET is also being actively worked on so may have other things by then. Compare current state only.
The truth is each platform has prioritized features relevant to its context. For what its worth in my experience while the JVM has many more JIT optimisations and the like it tends to need them more of them given the lack of some of those features you mention (e.g. value types). Whereas .NET code allows value types, better management of memory (i.e. Span), reified generics etc so the focus has been to allow the user to optimise themselves where required where still allowing for a decent performance default. Many of the optimisations in the JVM wouldn't have the same bang for buck in .NET and vice versa.
On a personal note I'm more of a fan of the .NET philosophy because the code is usually fast enough, and when I need to tune memory, avoid allocations, and do fast code it seems to offer more tools not in an unsafe context to do so. It allows a better "upper bound" of performance for core things IMO while keeping to bytecode/IL. Many benchmarks where the same level of application optimisation has occured from what I seen have confirmed this bias for me. YMMV
You could call `+` syntatic sugar, just like anything past assembler being that.
None of those are features, they were desperately needed shorthands for common java idioms. This is like calling braceless if/for/while statements 'features', when they're purely syntax sugar that has fallen out of favor completely because there's been several major security vulns found in major projects due to their use and development oversight/code review failure (https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-got...), to the point where most companies ban writing them.
Just off the top of my head:
* LINQ. Added to C# in 2007, Java didn't get streams until 2014.
* structs/value types, implemented in net framework and net core for 15+ years, but not present in java 17. Some oracle engineers have been working to implement them under project valhalla but it seems to be vaporware at this point after 8+ years of work.
* unsigned types, no plans to implement in java (though IIRC it was originally planned as part of valhalla)
* async/await
At the feature level Java is stuck in the mid 2000s. Nobody wants to do the actual work on keeping it competitive.
Thank God they didn't shove this crap in. When Loom ships (I believe the next LTS is their target, more or less), JVM will have the same concurrency story as Go does, without blue/green function split like in C#.
Not too comfortable to use functions that will compile to efficient byte code exists for them, but with the definitely coming Valhalla, it will be trivial to create a custom primitive class for unsigned ints.
> async/await
With project Loom, it will avoid the mistake of function coloring that async introduces. In a managed language, why not let the runtime automatically transform blocking calls to non-blocking, when it already knows what’s up?
Not saying it's a good or bad thing, but .NET and C# by extension has had these features for years (decades in some cases) while the only thing java has are half-baked prototypes and plans to 'maybe' implement things. In many cases these plans just get endlessly pushed back and new java major versions just become a pile of simple bugfixes which in the past were just pushed as minor jre updates.
In the background, coroutines are a strictly less useful feature than what will happen with Loom, and please show me any “promised feature” that were only half-baked prototypes? We do have one half of Valhalla (vector api) under an experimental flag with JDK 17 already, several language related JEPs were shipped already. That Loom and primitive classes take their time only mean that they are not vaporware, because these are actually ridiculously complex problems.
Streams are a library feature, records are a language feature. Streams could be implemented outside, records could not (no lombok abominations are not records).
comparing braces to records/multi line strings/switch expressions is so funny that I won't even comment on that.
Also, Oracle does pay plenty for the development of Java, and is a (surprisingly) good steward of the language, let’s drop all the blind hate.
And both in terms of GC and JIT, the JVM ecosystem is ahead, the reason they can be so head-to-head is that the CLR doesn’t hide lower level controls all that much, allowing for better hand-tuned programs (at the price of the resulting code not getting “automatically” faster in a possible future release)
In some cases, this may be your ops team.
Support for vulnerability patches, stability, and so on. You can automatically update and maybe that goes well and maybe problems are caught in testing. Regardless those events and changes have over time have increased costs. LTS should mean lower costs both through increased support focus, increased population of active users, and reduced forced change.
You're not wrong...
And many companies are cheap and don't buy - hence using JDK 17 won't give you LTS, buying support gives you LTS.
Non-LTS JDK doesn't have anything after 6 months.
And BTW. Azul MTS want's to have a word with you: https://www.azul.com/products/azul-support-roadmap/
e.g. JDK 13 and 15 do get builds.
Sometimes you're creating an app that other users need to run in their own environments - and in that case, targeting "latest LTS" is safer, as they may have requirements on what they can run that you don't.
Because now the feature releases can add new APIs rather than just new JVM features as they did under the old model, this process can be slightly more work, but still significantly less work over time. As regularly maintained applications update code and dependencies regularly, anyway, this is a clear win. Not only is the upgrade process easier and cheaper, but you get to enjoy the regular and significant performance improvements that have always been part of the six-monthly feature releases, but aren't in the LTS patches.
[1]: Those feature releases under the old model also added major new features with significant changes to the VM. For example, the G1 GC and JFR were added in feature releases.