5 ms·
Actually that post is very much a mistake. Everything in it is wrong. OOP is great for big applications which is where Java is still a leader. If you have stat
by invalidname 2y ago
Actually that post is very much a mistake. Everything in it is wrong.
OOP is great for big applications which is where Java is still a leader. If you have statics all over then you have a problem.
Heap was the right decision when Java was made. Newer JVMs are fantastic at allocating and releasing it in a way that beats C performance for some cases. There are edge cases where it can't do that which is why we're getting Valhalla which will solve that edge case.
The file API you're referring to is out of date. NIO access has been around for ages.
I agree that generics have a lot of issues, but Java has some of the cleanest error messages around. You dug up a single problem there which plagues pretty much any language that has a complex type system. The alternative is untyped languages that have their own problems.
Bytecode is fantastic. You're confusing historic VM implementation (the Pascal VM) which were inspirational but unrelated. The value Java provides there is amazing, first modern JVMs provide a level of performance that rivals native code. But the biggest benefit isn't in performance: it's observability and extensibility. Thanks to the stability of bytecode developers have built debugging and observability tools for Java that are both performant and powerful. Having worked in this industry I can tell you that no other language comes close to that power. This isn't even a contest, it's Java then everything else. It also opened the gate for many other languages that run on the JVM and interoperate with Java fantastically well.
Modern Java uses UTF-8 for Strings and for internal representation of Strings when applicable.
I suggest reading a bit about modern GCs. Xms/Xmx are important but modern heap allocation and the things done by the newer GCs is at a completely different level.
Misuse of checked exceptions in some cases is a pain point for me too. I'd also add that when Lambdas were added the designers didn't do the work required to solve the issue with checked exceptions there. Which is indeed a shame. Having said that, this is a wide problem in pretty much every language. Exceptions make a lot of sense in terms of performance/flow.
I disagree about finally though. It's a powerful control flow tool that allows us to remove code duplication. No, it doesn't look great. But the alternatives (e.g. Go) look worse.
- thomashabets2 2y ago> Newer JVMs are fantastic at allocating and releasing it in a way that beats C performance for some cases. And yet the care and feeding (tuning) of the GC consumes countless millennia of human time per year. This, as my post says, is not even an inherent problem with GC, but with Java assuming an eventually sufficiently smart GC. But also yes, successive Java GC implementations have done huge heroics. They've gotten better and better. A million PhDs have been minted inventing amazing technology in this space. But we've been promised the ultimate compacting pauseless GC for decades, and yet I see outage after outage of huge systems root caused to "look, we found a brand new way that the GC can explode (or just thrash) on us". And (for all its many flaws), Go chose a different strategy, and avoided a class of problems. They learned from Java's mistake in this case. There's still GC issues (though Go is also getting better and better), but nowhere close. > Java has some of the cleanest error messages around One can make the case for which is better (well, least bad) of Java or C++ error messages, but neither can come close to Rust error message quality and cleanliness. And Rust does have a developed type system. But fair enough, let's call it opinion. Which is a disclaimer I have at the top of the post. Like I said I'm impressed that Java managed to make error messages worse than C++, but you can disagree. > Modern Java uses UTF-8 for Strings and for internal representation of Strings when applicable. Ok, that sounds like I'm not up to date (probably nowhere near). It's not an issue I've fought in years. But glad to hear they fixed that… "when applicable". I'll read up on that. Thanks. > finally Go is not my favorite language, but here I think it's better than Java. RAII beats defer, but at least defer makes the programmer add the cleanup at resource acquisition, not four pages further down, possibly behind a null pointer check in case the finally clause triggers before the resource acquisition. I respect your opinion about aesthetics, but I don't accept that my opinion on finally/defer/RAII is "wrong". I appreciate the feedback. Thanks.
- invalidname 2y ago> And yet the care and feeding (tuning) of the GC consumes countless millennia of human time per year. This is a feature not a bug. When I write C++ code I need to make a lot of decisions in terms of memory behavior that would deeply impact performance. I can benchmark locally but production is the part that matters and I'm mostly blind there. An observability solution might be able to detect problems if I used the default C allocator under the hood, but in that case my performance would also be terrible to begin with. Java lets me detect production performance issues and even pinpoint them to specific objects in a shipping application. This can translate to feedback to developers that could be very valuable. But it also allows me to tune GC behavior in production to get better performance with zero code changes. Furthermore, the object deletion cost is deferred so if your app works in performance peeks it can be very efficient assuming it has enough memory allocated to it. The downside is that Java will probably always need more RAM than non-GCd languages (the amount varies). That's a reasonable tradeoff for most since RAM is still cheaper than CPU/GPU. > But we've been promised the ultimate compacting pauseless GC for decades, and yet I see outage after outage of huge systems root caused to "look, we found a brand new way that the GC can explode (or just thrash) on us. There is no "one size fits all". Java has such GCs but right now the default JVM ships with several GCs since every workload has different requirements. This means you need to tune it which is a challenge. If you see bad performance and don't know what to do then I would blame your observability provider. Good ones actually provide recommendations on how to improve a thrashing JVM. > One can make the case for which is better (well, least bad) of Java or C++ error messages, but neither can come close to Rust error message quality and cleanliness. Rust is great. But it is a young language with less baggage. It has no binary compatibility to deal with and it doesn't offer many of the language features Java (or for that matter C++) have. Those are all good things for Rust, but I wouldn't build the type of apps I build in Java using Rust. > Like I said I'm impressed that Java managed to make error messages worse than C++, but you can disagree. I just spent 20 minutes the other day looking at 100 errors in std::vector which I didn't touch at all due to closing brackets appearing in the wrong place... C++ error messages are at a different level compared to Java. Maybe I'm too used to Java but I never spend time on these things and the IDE is fantastic at handling 98% of the stuff. > Go is not my favorite language, but here I think it's better than Java. RAII beats defer, but at least defer makes the programmer add the cleanup at resource acquisition, not four pages further down, possibly behind a null pointer check in case the finally clause triggers before the resource acquisition. Go was built for simple quick solutions at the system level. Typically in such cases you would need to handle the error as it happens. Java was built for libraries/frameworks/APIs. E.g. if Spring runs into an error it can't handle it since it doesn't know whether you want to show the error to the user, recover, log etc. Throw is the only reasonable option. This lets me build a nesting doll of libraries and dependencies that can defer decision making and even add a systemic solution that eventually handles that. E.g. in Java I can define a recovery policy (retry/circuit breakers etc.) with an annotation and the system would handle all of that for me. In a typical Java backend application I don't write catch at all. You let the exceptions propagate and then write error handlers at the top level to define behaviors for the system/user. This also makes tracking issues in production much easier as you get a clean stack trace that includes all the details you need to fix most issues.