YMMV, of course, and yes, there are modern frameworks other than Spring (Play is supposedly pleasant to work with), but life is too short to try to sort out that mess.
I plan to stick to Elixir as much as I can, for as long as I can. When the language clicked for me, it was the biggest breath of fresh air since I decided to pursue programming as a profession, and even cooler than when I discovered Python/Django.
Edit: obviously, Java is not all bad, and not all (or even most) people who use it fit the description above. But something about the ecosystem seems to draw those types (no pun intended) disproportionately.
1. https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
Of course, it can definitely be attributed, at least partly, to just moving to functional programming.
Every day I look forward to 6'o clock, so I can stop working and continue on my own projects.
(Those 16 years are four years at Google, 10 years in a pair of startups, now 1.5 years at a medium-sized acquirer.)
But spring-boot has an almost zen like quality once you get that it favor convention over configuration. When I was a Java developer, I'd usually use spring-boot with the following dependencies to make the experience better:
- lombok: to generate the boilerplate: constructors, getters, setters, equals, hashcode...
- mybatis-spring-boot-starter: mybatis is a sql resultset mapper, you write the sql and it maps the results. I find that ORM like Hibernate or Eclipselink are a complexity trap: easy things are really easy but hard things are incredibly complicated, mybatis avoid that.
The problem I see is as far Java ecosystem goes Spring is big, respected dev framework to be used liberally anywhere. So if a developer like me call Spring a revolting piece of shit software it is just me asking to become jobless.
Because of that, there are a lot of Java devs.
Our team works in Go, and so we get a few Java devs in once in a while as new positions open up. The biggest change for them is to get out of archonaut mode and stop overdesigning everything. After going through a 3-6 month cleanse, it's fun to see them complain about the legacy Java stuff and how overly complicated it is and how slow it is to build and test compared to Go.
I love your use of the word “cleanse”.
I eventually graduated and my second job was a Java SWE.
I realized it's not just you sitting in a void on a theoretical BS questions. You have a team to talk to and get help from. If you forget what a HashMap is called, you don't get stuck trying to declare a HashArray with an automatic fail grade on the same level as someone who did literally nothing and handed in literally nothing.
Then I got a few years experience as a SWE and I realized my original assumption WAS correct. You are stuck in a void answering BS leetcode questions, and you DO automatically fail if you get stuck declaring a HashArray with otherwise perfect logic.
Indeed, technical interviews are so fun. Especially the fully automated ones, but honestly even the ones with interactive humans they still can't comprehend how somebody could POSSIBLY mistake a HashMap as a HashArray unless you were literally fake trash.
Do I use it for personal projects? Nope. Because it's not fun to "drive". For that, I pick the equivalent of a Mazda Miata (insert exciting car of choice), which for me is usually a Lisp.
Before, .NET only ran on Windows which disqualified it from many serious applications server deployments. Today, this is no longer the case for everything but cross platform GUI libraries, and even those are an option if you're okay with not having Linux support.
It's more akin to the old car you had just before you bought your Corolla. It was also pretty decent, but it was starting to have issues.
The JVM ecosystem is quite expansive and it's capable of running many things that would be a pain to develop in house (or pay a third party developer for). However, Java isn't the only popular JVM language anymore and you're no longer forced to use Java to interact with its surrounding ecosystem.
The language and the ecosystem around it aren't going to go away for at least a decade, but I think it'll slowly move the way of COBOL and FORTRAN: perfectly good languages, with perfectly good situations they outperform their competitors in, practically exclusively used for niche use cases and legacy software systems.
That could be true of any language that's achieved great popularity, but in 2022 Java is the language that dominates server-side development (by plurality, not majority), it is a technological leader in areas of compilation, garbage collection, and low-overhead in-production profiling, no other single language looks posed to seriously challenges Java's dominant position on the server (as PHP seemed for a while), and, at 27, it is more popular than COBOL was in its (rather short) heyday.
But it is true that Java is among a select set of languages (alongside Python, JavaScript, and C) that have managed to achieve a level of success sufficient to become "a COBOL" or "a Fortran" someday.
EDIT: typos
Might want to give some evidence of this. Also, C# has no plans of green threads. It went the async/await way which is a pain.
At least that is my experience running the only .NET team at a company that primarily runs Java.
I do not know if the problems are related to the Java language or to the typical Java programmers, but Java is the only programming language where I have seen a strong correlation between the programming language used to implement some application and a low quality of that application.
During decades of experience with various programs, whenever I was surprised that a program seemed to be abnormally slow, or it had an unusually ugly GUI, or it had a GUI that was impossible to customize, e.g. it was impossible to change the default font or the size of the font, or it had various other annoying features seldom encountered in other programs, I discovered that it was written in Java.
Most of these annoying programs where commercial programs, some of them very expensive programs.
The most recent Java problem that I have encountered was last year, when I could not install some expensive program, because the installer crashed immediately.
After some time wasted to discover the reason, the conclusion was that the installer was written in Java and it always crashed immediately on any computer to which a 30-bit monitor was connected.
That program had both Windows and Linux versions, but both crashed in the presence of a 30-bit monitor, so the Java installer was of the kind "write once, crash anywhere".
The workaround was to disable the GUI of the installer and make an installation from the command line.
There are some 15 years since I use only 30-bit monitors, but this was the first time when I have seen such a behavior (probably because I avoid Java programs, due to past experiences). Googling has shown that this was actually a well known bug in Java installers, which had not been solved in a long time.
The vast majority of Java (=running on the JVM) software these days is server-side, without any user interface.
Funny, I hear this sentiment more about PHP which deserved its poor reputation in the early days but is actually quite a nice language now and has been for a good long bit.
I think the biggest problem some people have with certain programming languages is that they don't "look" a certain way they prefer (syntax-wise), or don't have enough modern buzzwords associated with them.
Been a Java dev for ten years and I drive a Toyota Corolla....
...but also in my spare time I play around with Clojure and dream of buying a '69 SS Camaro
Here it is fine I think, the writer of the comment is just explaining their point of view.
Since we're throwing out analogies, Java-the-language is more like a riding lawnmower. It is capable of getting you to your destination, but will be slow and painful the entire time. There is probably some external constraint that you'll be forced to endure this, like losing your license.
https://www.fuelexpress.net/blog/just-for-fun/4597-2/
The Corolla is known for reliability and fuel economy. It's the car you'd buy for your fleet so you could ignore it, and then you would ignore it, and some day, people would start assuming you are going out of business or something because all your fleet vehicles are from the late 90s.
On the other hand, the 300 claims to be those things, but has terrible fuel economy, and apparently likes to unexpectedly spin out. They claim that new ESP magically fixes the problem, even though they've been claiming such things for years. It's actually supposed to be a luxury car or something, but apparently for some reason it is used as a fleet vehicle, where it is a poor fit.
Also, since it is a Chrysler, I assume it is unreliable. Oh look, I'm right:
https://cars.usnews.com/cars-trucks/chrysler/300/2011/reliab...
(In fairness, the newer ones are supposedly reliable. Sort of like how Java is supposedly pause free now.)
Filling out the rest of the analogy:
fuel economy => Java startup costs, and asymptotic / constant factor slow downs because it doesn't support zero cost abstraction.
spinning out => The GC, of course
magic ESP => The next GC solves the pause / thrashing issues (promised every year since at least 1998)
reliability => Constant API churn, the 8 => 11 => 17 => 20 treadmill, and (of course) log4j.
targeting luxury segment => Java targeting toasters, IoT and web browsers back in the 90s, but only being able to run on hilariously overbuilt and power hungry enterprise boxes instead. Also, the classloader's open world assumption.
There are over three billion active Android devices in the world, since 2014 they have been running Android Runtime runtime environment, which uses Java (or Kotlin) bytecode. Before 2014 Android used Dalvik, which did the same thing. This is running on some pretty low powered devices.
I won’t even correct the others, but how come Java is unreliable? Like, I have a hard time thinking of anything in the category of computer programs that would fair better.
Go uses two build tools for any non-trivial projects. One write in go.mod and another in Make :D (see this - https://github.com/kubernetes/kubernetes/tree/master/build/r...)
people shit on maven, but i say it's much better than many other built tooling - npm, make, or custom scripts.
The only thing need getting used to is that you cannot and should not stray from the maven model - fit your project's built into the maven model, rather than try to twist maven to do your bidding.
It’s primarily used by enterprise shops. You’d never brag you about driving a van. It’s not fashionable. It’s kinda ugly.
But…it gets the job done.
> To me Java is like a garbage truck. You get to work, start it up, do a nearly invisible but absolutely essential duty, then, at the end of the day, you turn it off and go home. No one dreams about garbage trucks or puts one in a car show but they’re there and ready to go right back to work when you are.
I get frustrated with JSON because of things I could do in XML that I can't do in JSON without breaking the spec. And that doesn't even cover how annoyingly verbose anything done in JSON is.
Anyways.
A Chrysler PT Cruiser. Odd looking, beloved by its fans, and a reasonably adequate (if quirky) to drive.
Once they take that responsibility, the debate will be over because:
"While I'm on the topic of concurrency I should mention my far too brief chat with Doug Lea. He commented that multi-threaded Java these days far outperforms C, due to the memory management and a garbage collector. If I recall correctly he said "only 12 times faster than C means you haven't started optimizing"." - Martin Fowler https://martinfowler.com/bliki/OOPSLA2005.html
"Many lock-free structures offer atomic-free read paths, notably concurrent containers in garbage collected languages, such as ConcurrentHashMap in Java. Languages without garbage collection have fewer straightforward options, mostly because safe memory reclamation is a hard problem..." - Travis Downs https://travisdowns.github.io/blog/2020/07/06/concurrency-co...
"Inspired by the apparent success of Java's new memory model, many of the same people set out to define a similar memory model for C++, eventually adopted in C++11." - https://research.swtch.com/plmm
This combined with the fact that Java doesn't crash and you can easily hot-deploy the classloader (maybe 100 lines) means nothing can compete that doesn't copy everything Java does VM + GC (hello C#, please don't downvote).
To use anything else (than JavaSE without heavy deps.) on the server is madness.
I once worked on a Java-based server at Google that had to answer requests with millisecond latency. In order to achieve this, the server had to block garbage collection most of the time. Periodically, each instance of the server would ask the load balancers to stop sending requests to it, so that it could then safely run the garbage collector, and then ask for traffic to return.
We likely would not have used Java had we foreseen needing to do this. I believe some time after I left, it was all rewritten in C++.
Sadly the garbage collection debate is full of people who want GC to be the answer to everything, and have chosen their arguments and beliefs through confirmation bias to support their desire. Many of these "GC is faster!" arguments come from that, but it just ain't true most of the time. Performant GC is incredibly complex and incredibly complex systems tend to fall over if you don't use them in exactly the right way.
Worse, GC is extremely bad at managing resources other than local memory. In complex distributed systems or other software that manages external resources, GC languages tend to be ill-equipped for the job because they lack explicit resource-management tools like RAII. In Java, you sometimes see frameworks where everything has a `dispose()` method and complex systems are built to make sure that `dispose()` method gets called... this is a failure, it should be handled by destructors.
GC is generally nice and convenient for application engineering, but a poor fit for systems engineering. The boundaries between the two are admittedly fuzzy.
This is kind of a fair comment, but kind of not, because Java performance and GC internals have really advanced a lot in the last decade. It would really help if you qualified approximately when this was.
> Sadly the garbage collection debate is full of people who want GC to be the answer to everything
Good point. I think more recently, the Java world is very aware of this. Native and off heap memory has been getting more use in performance sensitive stuff for quite a while. You can totally just (essentially) malloc and free in Java, if you really need to for performance.
That said, if you want to be able to make your code accessible to a wider audience, GC is a must. There are tons of junior and midlevel developers who don't really have experience working with non-GC application code (and in many cases are intimidated by it!), and you will be restricted from hiring any of them if you use a non-GC language.
If that's ok, then that's ok, but with Java you can still do a little off heap stuff in the critical part you need to, encapsulate it behind a safe API, while letting the juniors run amok in the rest of the codebase.
You're getting into hard real time territory there. That's a different universe and will require special considerations even in C. I think it's fair enough for people to discuss server performance and assume that the context is our normal programming universe.
That's... not really normal, though, and sounds like the exception that proves the rule. For the vast majority of applications, Java will perform better, be easier to develop, and be safer to run, than an equivalent server written in C or C++.
At my previous job we used to run realtime audio through a Java server (RTP streaming). I do remember in the Java 6 days that the GC would need tuning to ensure that GC pauses wouldn't delay audio to the degree that there would be dropouts or perceptual delays. But with Java 8 (and later releases), which came with better GC implementations, those problems just went away. Sure, realtime audio is usually fine with even up to 100ms pauses (or even 200ms, sometimes) -- so this is much more tolerant than your sub-ms example -- but we rarely saw anything even remotely that long with the more modern JVM GC implementations, without really having to tune behavior that much, or at all. Meanwhile, P99 stats for most JVM services were in the low to mid tens of milliseconds, and anything longer was always due to calling out to external services, like relational DBs.
For the rare case like Google's, sure, it's absolutely expected and appropriate to need to use a non-GC'd language instead, at least for some things. For pretty much anyone else, the JVM is more than adequate, or can be made adequate with some reasonably simple GC tuning.
> Many of these "GC is faster!" arguments come from that, but it just ain't true most of the time.
I don't agree. I think it is true most of the time. But I think many people don't think about what they mean by "faster". Faster as in throughput? Sure, a modern, performance-oriented GC (like the JVM's) can very easily beat manual memory management there. Latency? Well, ok, that can be a bit harder, so you need to evaluate things on a case by case basis, and possibly do some GC tuning to get the latencies you need. But even then, you can usually do just as well (or better) on the latency axis as well. Just not always. But I don't subscribe to the "But sometimes..." school of objections. Yes, sometimes some technologies don't work for certain use cases. That's fine. Choose your technology wisely. But in Java's case, it really is just "sometimes". Not most of the time.
Let me reiterate, though: Google is not the common case! By a long shot! It is an outlier, and it's expected that a company like Google will have to deviate from the mainstream to reach its performance targets sometimes. But also consider that (from what I understand) even Google has a ton of services written in Java, and they... work just fine, no?
> In complex distributed systems or other software that manages external resources, GC languages tend to be ill-equipped for the job because they lack explicit resource-management tools like RAII.
Eh. I initially thought of this as a problem, but in practice, I've rarely run into an issue with this sort of thing. Maybe it's because there's still vestiges of the C programmer in me that will always think about memory ownership and lifecycle, even when writing in a GC'd language, but I've rarely had my own issues (or seen issues written by others) where someone has forgotten a `.close()` or `.dispose()` on something. The "try with resources" syntax can help here too, even though IMO it can be kinda cumbersome.
And as much as the Java docs tell you to essentially never override `finalize()`, it can be a useful "last ditch" tool to ensure that any "manual dispose" owned references get cleaned up, and you can also add logging there; I'll often do something like "Got to finalize() without disposing of Foo instances: BUG!". I also appreciate when third-party authors who write `dispose()` methods also do this. It's not perfect by any means, but IMO the convenience of relying on garbage collector rather than manual memory management far outweighs this downside.
Lately I've been writing a lot of Rust, and I'm enjoying the sort of "middle ground" approach, where I don't have to think about memory management as much, but don't have to worry about GC performance, either. Certainly Rust doesn't eliminate these concerns; I still need to think about ownership and object lifetimes, but it's never "oh crap, I forgot to free() something and there's a memory leak", or "oh crap, I tried to use something after free()ing it and crashed", it's more like "ugh, rustc doesn't agree with me that this object lives long enough and refuses to compile it". Annoying, but I'd rather find this out at compile-time than runtime.
But then I'll go back to writing Java or Scala after being in a Rust project for a while, and remember how nice it is to just not have to think about these things.
> ...but a poor fit for systems engineering.
Absolutely agreed. But I would not call writing distributed network servers "systems engineering", even though I do agree that the boundary between systems and applications engineering is indeed fuzzy.
- a much better concurrency model, that gets you parallelism for free just by adding cores
- no global gc pauses, low-latency
- fantastic operational tools (trace debugging, remote shell, etc.)
- Erlang/OTP gives you great middleware out of the box, including including queues, pub-sub, service monitoring, database, etc.
- Honestly just a more expressive core language.
All that said, I don't really have anything against Java. It is a great language for the server, but it is not the only choice.
"a much better concurrency model"
If your program is CPU-bound, where real threads are king, then Erlang is a pretty poor solution. Because you cannot have real threads in Erlang, even if you wanted to. Number crunching or string processing are not its forte.
Also, most enterprise software does not really benefit from green threads anyway, because the scale they are running on easily handles blocking JDBC and http request calls. Not all companies are google or want to run a telephone switch that handles >million concurrent calls...
And finally, if you go Erlang, you get actors, whether you like them or not. You cannot have CSPs, for example, which in many ways are superior to actors. You don't get to choose your concurrency model, it is chosen for you. If your use-case suits this model, great. If not... not so much.
ZGC has sub-millisecond pauses https://malloc.se/blog/zgc-jdk16 And afaik azul's C4 collector has no global pauses, only per-thread pauses (which are also short)
Java also has this for at least a few years now. G1GC and ZGC in JDK17 offer no global stops unless absolutely necessary, and ZGC even has latency targets.
Erlang can only scale 1-to-1 things like phone calls.
Java is the ONLY language that scales many-to-many with stable non-blocking IO and concurrent parallelism that shares memory atomically AND doesn't crash.
So the actual tradeoff is more whether you want better throughput or tail-latency. To improve the latter, you have a singular command line option of using ZGC as well.
[1] https://inside.java/2022/05/30/sip053/#:~:text=ZGC%20was%20d....
[1] Reduced them to always well below 1 millisecond.
Java's GCs are incredible and you have a menu of algorithmic options that let you avoid whatever problem you're worried about.
True concurrent GC have been available since late 2000s. Azul had a read-barrier GC as well - effectively a pauseless GC (or pauses under 1ms)
This is a feature, not a bug. Which is riskier?
Huh? Doesn't crash in what way vs. C? I can still deref a null pointer and blow up.
Java's perfectly fine, and I have no idea why you'd write C any more, but if you care about (extreme) performance and not crashing, Rust seems the obvious modern choice here.
Sometimes, you want to fail fast and be forced to fix that bug. More often, I want to keep doing whatever I can to test and develop the system and find more bugs without restarting the whole thing.
Java does not have undefined behaviour at all. Dereferencing null would throw NPE which is ordinary exception, completely fine to handle or suppress or whatever. There's no concept of uninitialised memory. The only sources of undefined behaviour in Java is calling native code or using unsafe methods which are very rare and usually located in well tested library code.
Even stack overflowing is defined behaviour and you can easily recover from it.
That's a terribly broad generalization. There are multiple other options that are entirely sane.
Experienced C developers have switched to it. Actually it gets even better, they are extremely productive in it. Don't trust me, check github.
I wonder why. /s
Will the next WhatsApp be written in Java?
i think what has always sort of steered me away has been a few things:
1) i've long been interested in snappy, interactive and realtime local and server things. and the jvm can do this stuff, but usually it's better suited for server side use where it can be warmed up appropriately. this may also just be a personal dogma that i need to get over.
2) every language and runtime has its quirks where you have to breakdown the illusions of the language itself and understand the internals to get the best performance, but i feel like java is the worst instance of this, where the tricks that people go through in order to control the gc seem excessive.
3) i like simple and concise programs where i feel like the java ecosystem encourages code sprawl. hello world has so much weird stuff and a serious java project has thousands of small source files where computer aided programming (ie. a proper java ide) is not really optional. i find this not only to be unergonomic, but also challenging for being able to quickly understand what a foreign codebase is doing in a short timeframe. maybe it's different from the perspective of a primary developer on one of these large projects though (and possibly quite pleasant).
4) for some reason, i've always liked feeling closer to the metal. even scripting languages "feel" closer because of the jvm abstraction. this is probably also a personal dogma.
> He commented that multi-threaded Java these days far outperforms C, due to the memory management and a garbage collector.
people who like java say these sorts of things all the time. i haven't seen a head to head comparison that confirms it though. (although i have not looked). regardless my understanding is that once a jvm is warmed up, it can be quite performant-- and that it has opportunities for live optimization that you don't get in a classic c/c++ runtime.
i don't think c/c++ will be going away as long as operating systems are still written in c. a java operating system seems... strange to me. but that said, i think java has proven itself in terms of high performance application server software.
Except in very narrow circumstances, I've not ever seen Java perform at the level of C or C++, especially when I/O is involved. Java typically needs 2-4x the memory and a lot more cores to do the same job. That's fine if you bought the machine and the machine is big enough, but if you're leasing an EC2, why pay more every hour?
These days, my C, C++, and Java days are behind me. We do mostly Go, and couldn't be happier. The GC and language have really good mechanical sympathy, and the bad memories of tuning Java GC are fading. We pay a small tax in terms of CPU and memory over using C, but not remotely like what we saw with Java. And yes, we built prototypes of critical use cases in C just to see how 'bad' Go would be, and were very pleasantly surprised that Go compared very well indeed.
I would have thought that in 2022 the failure of Java to conquer any kind of market on the most popular platforms and 2nd most popular or any kind of popular plus the rise of languages like Swift, Go or Rust would have made that point perfectly if not painfully clear. Even in Android, Java’s big success story, both the VM and the language were either outright replaced or made legacy.
I suppose it’s a matter of perspective, but I like to think of the server not as Java’s last kingdom, but more as Java’s last refuge before being finally banished.
Note that I don’t dislike Java. I’ve used it successfully on Android and it helped us achieve our goals of writing a passably performant application. Neither do I think it’s a bad technology, like many would argue. It is very uninspiring and bureaucratic though…
Also, wondering how much of that overhead in the C program is due to malloc (can be overcome by memory arena)
Please explain. What kind of crazy jack write servers in C? (Real question, trying to learn here)
The problem Java experiences (not 'experienced') is that it isn't the 1990s anymore. People aren't easily hyped, not smart people at least. Programming language research, already way ahead of Java in 1995, moved far ahead some more. The internet and open source and free online education and social media means smart language designer(s) can now begin a baby project and put it on github and wait their luck for a corporation or organization that will support it, and they can wait a decade+ on hobby mode till the luck comes. Java's competition isn't C, I highly doubt it ever was, it's dozens of far superior programming languages that were born after 1995, and some of those that were born before but weren't very populer because internet and open source barely existed in the 1990s and they weren't made by a corporation.
Even if you keep overfitting the requirements function time and time again to gradually approach java (by adding "vast libraries" and "performance" to it, although neither of those things are specific to Java), you will eventually reach a very narrow niche with Java and Kotlin squarely in the middle, I know which one I'm going to choose if I'm willing to maintain my sanity and not drown in 'paper-work programming' that is the developing experience with Java. It's as easy as opening a source file in an IDE and naming it with a '.kt' extension rather than '.java', I'm in love with it.
Companies use Java extensively ? So it's a COBOL then: Old, everywhere, infrastructure-critical, but an utter failure as a programming language in every way that is not "runs my old program I'm too afraid to re-write". This is forgivable in the case of COBOL because it's a literal proto-language, one of the earliest of the species. It's not forgivable in the case of a language designed when Smalltalk and ML and Haskell were around.
The history of Java is the story of a corporation stumbling upon the idea of a VM (1960s stuff) and deciding that it's so good that they need something like this under their name right now, and not giving the slightest shit about the design of the language that sits on top of said VM. And they were right, the idea of a VM is really so fucking good that Java's aweful design was tolerated, until somebody relized running on the JVM no more means writing Java than running on x86 means writing x86 assembly, and wrote the first non-Java JVM language. And because VMs are so fucking beautiful, you get all Java code for free.
>doesn't copy everything Java does VM + GC
Come on, write those 2 words on any half-decent academic research repository and you will get hits from the fucking 1960s. Java is younger than Lisp, Smalltalk, Self, Haskell, Python and the same age as Ruby. All of those languages do VMs and GCs. Java's hotspot is taken from Self, that's documented stuff. And "Copying" isn't copying if it's a better implementation of the interface, JSON didn't "copy" XML, they reimplemented its interface better.
I think python is the same way, the ecosystem is so rich that you can really do anything you want (until you get into low latency).
You can do better in your own code, but you still have exposure to the code in the library ecosystem.
Worth it, though. It really does seem like there's a Java library for everything.
static void main() {
BiFunction<Integer, Integer, Integer> add = (Integer x, Integer y) -> x + y + 5;
Integer result = addTo(10, add);
Integer result2 = addTo(10, (x, y) -> x + y + 5);
}
static Integer addTo(Integer acc, BiFunction<Integer, Integer, Integer> addFn) {
return addFn.apply(acc, 5);
}
Integer eval(Expression e) {
return switch (e) {
case INT(var value) -> value
case ADD(var left, var right) -> eval(left) + eval(right)
case MULT(var left, var right) -> eval(left) * eval(right)
}
}I see companies downshifting to unmaintainable toys such as Python even in data engineering circles. It's really odd that mobile developers with Kotlin (and front-end ones with Typescript) are getting ahead of backend ones in adoption of modern languages.
Once Loom and Valhalla get merged to an LTS the remaining vestiges of bad old Java will have been gone. I really hope Graal goes mainstream too. That will hopefully blow out of the water the golangs of the world. But those are platform-level improvements any JVM language will benefit from.
If the JVM is considered low latency I shudder to think what is high latency.
Aside from Java's innate finickyness, it has been an unexpected pleasure. I think a lot of it has to do with its static typing (I typically work in dynamic languages, and it's nice knowing that if the program compiles it likely works), and how simple the language keeps its primitives.
But you need really good tooling to use it, like a powerful IDE with good autocompletion and refactor support. It is way too verbose to type everything out yourself, and the verbosity means manually refactoring takes lots of changes around the program to manifest.
The sheer amount of code out there to import is immense, there seem to be libraries for anything and everything!
So far, it seems like the time I lose to its pickiness, I gain back with IDE features, static typing, and the ease of understanding it (because it is so verbose).
I'm also not hot on how it seems to steer everything into a factory pattern, but so far I've been able to avoid that for most things.
One of the ironies of Java has been that its strictness and verbosity can make it hard to develop, but that strictness also enabled the development of powerful IDEs. What feels like a hassle for a 100 line file becomes an asset for a million-line project because of the safety, discoverability, and refactoring it enables.
In c# when I look at other people's projects I often feel like it's a foreign language. You can make it look like C++ with unsafe blocks. You can nake it look like ruby with dynamic variables. You can write sql-like statements.
As much as I love writing in C#, I prefer reading other people's Java.
This is true and hard to communicate to people that haven't lived it.
If you want to see how much progress there has been in production-quality statically typed languages, write some multithreaded code in Rust. In addition to being memory safe without a GC, the compiler also confirms that your code is threadsafe.
Both those guarantees can be violated using the "unsafe" keyword; Java has similar mechanisms that break memory safety. Java doesn't provide meaningful thread safety, in the same way that C's malloc/free don't provide meaningful memory safety -- it's possible to write thread- and memory-safe programs in both languages, but the Java compiler doesn't really help out much with thread safety, just like the C compiler doesn't typically check for use-after-free, etc. Go's thread safety semantics are closer to Java's than Rust's (though multithreaded programming in Go is more ergonomic than in the other two languages).
Unfortunately Rust doesn't have the kind of library support that Java does, and I don't really want to roll my own on some things (the side project will never get done if I get lost in the weeds)
For this project the choice fell between Java and C#, and Java won because I was familiar with the library code that does what I want to do.
Besides, Java has Swing, which is about the only cross-platform GUI toolkit I can stand to work with (GTK as a close second)
> but it also has heaps and heaps of loopholes in the type system (that turn into runtime exceptions)
I've yet to run into this in places where I don't really expect it. Do you have some examples of where this becomes a problem? I wrote a bunch of reflection code that triggered a lot of RuntimeErrors, but that was foreseeable as its reflection, the whole point is to figure out types and whatnot during runtime. And at that point, I just fall back to how I write code in dynamic languages.
No, not at all. Rust verifies that your code has no data-races. That is an absolutely tiny subset of all race conditions, that are simply not verifiable statically.
I do intend to dig into it a bit more once I feel like I have mastered Java
If only project Loom will get out there so I wont be coding everything like Javascript I'd be happy. :)
https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
- IDE: IntelliJ and before that, Eclipse, were painfully slow to use. Even now occasionally if I have to launch Android Studio I have to wait for Gradle and various other things. The entire IDE gets sluggish while it's doing indexing and all sorts of weirdness. vim integration was quite poor at the time, I don't know if things have changed since.
- Verbosity: It always feels like I am writing boilerplate and long names. I remember trying to write something with websockets and no matter what library I picked there was a ton of boilerplate to write for just connecting to a socket and sending a message.
If given a choice I'd much rather write in Python, C#, Ruby or even Typescript. The language feels very dated - or perhaps there are newer ways to do things that I'm totally unaware of.
Minimum RAM for a sever doing something normal over tcp is measured in gigabytes.
Big servers > 16gb get difficult to manage at runtime.
You have to scale with more VMs.
You can write useful C servers that are very small, especially if you compile with musl. And run the same code for 1000kcc (not a typo)
When you have big arrays of memory storing everything as Objects/pointers gets messy and inefficient. But any big heap is hard to manage and keep response times consistently low.
My other gripe os that "write once run anyway" is no longer close to true, since Oracle. Mac, Linux x64 and Windows 64 are your only sane options. If you look at the compile targets list for c or rust you can see write once run anywhere working on a lot more cpus. True, you might have Arch specific code but it works, and most Java does not port from Linux container to Windows for example.
A statically compiled Binary is often easier to move across systems, because a java app is rarely a single jar. It's usually >5gb of app specific jvm and libs and config files.
> When you have big arrays of memory storing everything as Objects/pointers gets messy and inefficient. But any big heap is hard to manage and keep response times consistently low.
Java can handle very large, TB-sized heaps (under normal circunstances, about 52TB). But when you're dealing with TB-sized heaps, anything is slow - including programs not made in Java.
> You can write useful C servers that are very small, especially if you compile with musl. And run the same code for 1000kcc (not a typo)
Anyone can build stuff with musl, but musl is in general a lot less optimized that GNU glib, so I guess it really only makes sense on resource-constrained environments - not those where you're handling TBs of heap.
> A statically compiled Binary is often easier to move across systems
I guess you're talking about jart/cosmopolitan here, not your average musl application - a statically compiled Binary usually needs to be recompiled to work on other systems.
> because a java app is rarely a single jar.
Uberjars are very common, the norm those days.
> It's usually >5gb of app specific jvm and libs and config files.
On my experience even if you "accidentally" bundled the whole Java SDK together with your app, you're not getting over 250MB for Java.
Unless you're counting the whole OS, in that case, even the musl application isn't that small anyways.
While a .class file will technically run on any jvm. A java application is usually more complicated and Uber jars don't work if you have stuff in meta-inf e.g. Spring.
I am primarily a java dev and I have no tools or own code that run with java -jar some.jar.
Some of my Java services run with around 256-512 MB of RAM per instance, in Spring Boot containers (though Quarkus and Dropwizard can be even more conservative outside of enterprise bloat situations).
That said, I'm inclined to agree in general, as I've seen monolithic Spring applications that refuse to run with anything less than 2-4 GB of RAM given to them.
Perhaps even worse yet, there's no way for you to properly cap the resource usage within containers so you'd end up with more aggressive GC inside of containers but without OOM issues, when they're given a memory limit. -Xmx regularly gets exceeded because that's only a part of the equation and the creators of JVM didn't really think of a case where you should be able to give the whole thing a number of MB/GB it's allowed to use, which would never be exceeded, whatever it takes.
> My other gripe os that "write once run anyway" is no longer close to true, since Oracle. Mac, Linux x64 and Windows 64 are your only sane options.
Agreed, then again with most "business" software running in x86 OCI containers seems like the only sane option. Though I guess it depends on the environment that people have grown accustomed to and how they've been burnt in the past (e.g. I'd never want to use package managers or worse yet, extract random tar/zip archives for "business" software, never install Tomcat on the system directly etc., only use containers that are 1:1 the same in all environments where they're run).
Of course, web dev is just a part of the greater picture, so there's lots of nuance there as well. Personally, SIM cards running Java seems like insanity to me, though: https://en.wikipedia.org/wiki/Java_Card
Then again, I'd say the same about Python implementations that are geared towards embedded setups, personally even Go seems like a better fit if someone didn't want to use C/C++/Rust, though platform support would still be a relevant question.
wut? do you mean MB? most small web services(jetty, jdbc/pg driver/json) I have work on use below 500MB
If you apply some discipline, I think Java is a great language.
Today I'm a software engineer with experience in JavaScript, Ruby, Python, Elixir... maybe it's time for me to give Java another try.
Nah. If you have to use it, Java's really not that bad. But I would never choose it for a personal project. Its' strengths are in all of the concerns that come with enterprise development.
Same here. Nothing more to add.
All that said, I don’t use Java much anymore, preferring to use Clojure when I need the rich JVM ecosystem. I do follow new Java language features and usually try them.
It really shows which languages can re-invent themselves (Java, .NET, PHP, ..) while some fail (Fortran, Basic, Pascal, Perl, ..). Not sure where JS is ;). Python seems to have survived the 2/3 schism by now.
My most recent small (1k LoC) Python project is “fully typed” and therefore practically type safe (not strictly speaking though). Lots of the large libraries are typed as well, which is important.
For Python, work around the GIL might be the next evolutionary step, that time in the name of performance.
I have to add that JavaScript would greatly benefit from a solid base class library
I think some developers took "write once, run anywhere" as a challenge, which is why I still have to keep virtual machines with ancient Java versions around to configure and use certain remote IP-based KVMs, certain IPMI functions, certain older fibre channel switches, certain poorly thought out IP cameras, and so on.
I don't worry about security problems in the JDK or major libraries any more than anything else. I'm not sure where your security concerns are coming from.
Stuff like Microprofile, Quarkus, ActiveMq, Tomcat, and even JakartaEE are gravy on the cake.
The real reason why Java is a champ is because outsourced labour uses them. https://blog.jetbrains.com/idea/2020/09/a-picture-of-java-in...
It’s one of a few frameworks that makes writing Java less tedious again.
Java didn't evolve as a language for a while and that left the door open to other languages and other non-JVM ecosystems a lot. As the article notes, Java 8 was a breath of fresh air, but it was minimal in some ways. Lambdas and streams were great additions to the language. However, Java 8 came out in 2014. That's quite late to the game, in my opinion.
C# is probably the closest competing language/ecosystem (albeit constrained to Microsoft for much of its life). In 2007 (7 years earlier) C# 3.0 had lambdas, the `var` keyword, properties, object initializers, the equivalent of streams (and really better), nullable types for value types (like int), etc. In some ways, Java has caught up - and C# lost a lot of time being constrained to the Microsoft ecosystem. However, in other ways it hasn't.
I just want a POJO: frankly, this has been a problem that Java hasn't solved and it's been well over a decade where everyone has known it's a problem. No, records don't solve it. In Kotlin, I can make a data class and it's easy. In Scala, I think they're case classes. In C# I have properties where I can say: `class Person { string Name { get; set; } }`. I can see that it's just a boring property without having to look at method bodies. If there's something special, that get or set can have a body to do stuff and it becomes really clear that it's something special. Getters and setters are a wonderful way to set traps for others on your team or for yourself a year later because you look at a class with 15 items and it's going to have 90 lines of getters/setters + another 30 lines of an empty line between each method. You look and just decide "yea, I'm sure this doesn't have special behavior" and go about your business just to get bitten later.
I want to be able to instantiate data easily: With Java, I can do `var person = new Person(); person.setName("Johnny");`, but that becomes pretty tedious and error-prone when instantiating a large object. With records you have a constructor, but then you're dealing with positional arguments and it's hard to understand. When reading the code, you don't necessarily know what each of the inputs means. Maybe your IDE puts the argument names in. When filling it out, I've found IDEs to only be somewhat helpful. With C#, I can do `new Person { }` and then hit the suggestion key combo inside the brackets in my IDE and it'll offer to fill out all the properties so that I get something like:
new Person {
Name = "",
Age = 0,
Address = ""
}
That means I don't forget about fields (as can happen if you're just doing `person.setX()` all the time). It's easy to see what is what when reading it. I can delete fields I don't want to initialize at the time. Yes, maybe immutable objects are the One True Way, but C# lets me choose (I can label properties with an initializer `init` rather than a setter `set` and then they're immutable).Kotlin offers stuff like this too because it's really useful toward creating code that's easy to create and maintain. Go also lets you initialize structs in a similar fashion.
Java has come back to us a decade or more late with records. They're not bad, but they're only offering one thing. They don't cover what C#, Kotlin, Go, and other languages have offered for so long.
The annoying thing about Java is that it doesn't feel pragmatic a lot of the time. It feels like the language hates stealing ideas from others. It's Java: people steal ideas from Java, not the other way around. People do crazy things just to get POJOs including Immutables (http://immutables.github.io), AutoValue (https://github.com/google/auto/), Lombok (https://projectlombok.org), Joda Beans (https://www.joda.org/joda-beans/), and maybe more. They generate lots of code at compile time or do funky runtime stuff.
It just feels like Java misses the pragmatic stuff and still kinda doesn't want to handle that. I feel a bit silly harping on things like POJOs and setting data on a new object, but that's a big part of day-to-day stuff and it definitely pushes users away from Java towards languages that seem "better" simply because they don't have Java's oddly strong attachment to not offering simple value objects. Yes, again, records do something - but it feels like Java ignored how people are using Kotlin, Go, C#, and more and didn't go for something that would have been as widely applicable and pragmatic as it could have been.
Java has a lot of great stuff like great GCs (yes), lots of cool research, great performance, and Project Loom is really exciting. I just wish the language would lean a little more practical.
In general there has been quite some effort to allow a programming style in C# with less of the ceremony and verbosity that is often associated with C# and Java. And to me this does make working with C# more pleasant.
I like to believe that records with the upcoming ‘withers’ will be an adequate answer (if a bit too late) to this whole question.
I know we like to pretend that Java now has first-class functions since Java 8, but without easier to use functional signatures they're just too much of a chore. Who the heck wants to write a new interface in a separate file to do this.
I'm grateful for the JVM and think it's probably better than the CLR for targeting a high level language (as evidenced by the continued existence of Scala, Kotlin, and Groovy). But Java, even post-Loom and post-Valhalla, will never be the promised land for quick-to-read-and-grok code.
Is it better to implement something natively in a compiled library and link it in from Java or better to rewrite it in Java? What about lifetimes of objects - who "owns" an object - the runtime or the lib?
To me it’s always felt like some kind of assembly language humans shouldn’t deal with directly.
This one, existing technology would make Java DX on par with the dynamic languages
The biggest thing missing in Java is an answer for the billion-dollar mistake. Real world Java is plagued by NPEs because a lot of Java is written by low caliber programmers. Java + functional error handling would be a monumental improvement.
Now let's talk performance of most commonly used web frameworks. I will save you the trouble of reading long text. Just check https://www.techempower.com/benchmarks/#section=data-r21&tes... and search the tabs for the more popular Java frameworks like Spring, Spark, Struts, Grails, Wicket, etc.
You may find yourself surprised, finding most of them in the bottom 25th percentile. Now scroll back to the top of the page and check where ASP.NET Core is. That's right, more often than not, in the top 10. All that performance, and you get it out of box just by using defaults and then some more. The only exception I see is Vert.X which is both mentioned across the web and also present in the top of the list.
Now, you may say that it's not very representative and there are entries of dubious usefulness in production scenarios (looking at you Just.js). And you would be right. However, the way to get most performance from ASP.NET Core is not by using tricks but rather simply writing code like in Node.js with app.MapGet("/users", delegate) and friends.
Despite all this, I still think JVM technologies like Hotspot or GraalVM have an upper hand over what .NET JIT/NAOT is capable of. However, keep in mind the out-of-ordinary performance gains that C# gets with each subsequent release. In areas with significant possibility of improvement like arm64 codegen quality, moving from .NET 6 to upcoming .NET 7 will yield you up to 40% performance improvement from JIT alone. And it was done in significant part by changing the code of JIT/Runtime that used to be x86_64-first to being cross-platform oriented (e.g. Vector codepaths becoming plat-agnostic, correct atomics being emitted for ARM, etc.).
.NET Framework used to be stagnant. After becoming OSS, .NET is the polar opposite, getting significant improvements in all of its areas with each release be it runtime code, standard library, language features or supported usage scenarios.
I think today, C# and .NET are mostly being held back by decades of legacy libraries and decisions, which you may consider avoiding in favor of newer solutions, regardless if those are in BCL or community-driven libraries. Still, sometimes people simply use code in such a way that unreasonably kills its performance. But as long as you avoid known gotchas, your C# code will easily perform in production at the speed of Rust, C++ or C.
In Java, all methods are virtual by default. Java also does the equivalent of a vtable lookup at runtime for function calls, but it has something C++ doesn't have - the hotspot optimizer. For any call site that is executed enough to affect runtime performance, the hotspot optimizer will optimize away the vtable lookup if there are only 1 or 2 method versions called at that site at runtime. This is true for the vast majority of cases. For most of the other cases, where you have 3 or more possible method implementations that could be invoked at a given call site, you would probably have to have something like a vtable lookup at that call site whether you use OOP or not (switch statement, if-else, explicit table of function pointers, etc), so you're not losing performance there either. The end result is, the JVM gets polymorphic methods basically for free in terms of performance.
This is just one example, there are many other clever things the JVM does to make OOP code performant. I don't have a citation, but I do recall seeing a talk (maybe by James Gosling?) where he mentioned that one of the primary design goals of Java was to make "doing the right thing" from an OOP perspective also the best option for performance.