Earlier quoted context omitted.
I ported a moderate sized java project to golang. Test suite runs order of magnitude faster now. There isn't much change in terms of the architecture. Pretty much the same algos and data structures. The whole dev tooling runs on a 16 gb mac without swapping now. I used vs code for both
So you compare the build tool of A and B on a short-lived job type where java is knowingly not its strongest? How is that a meaningful comparison?
Java 27
371–380 of 424 posts
Re: Java 27
#372Re: Java 27
#373Earlier quoted context omitted.
> I use Java every day but just to point out that your info about Go‘s GC seems out of date. I'm well aware that Go's GC has improved, but the moving algorithm was designed not just to be fast for a GC, but to be faster than no GC. So Go's new GC is good - for a mark and sweep collector. But it can't compete with a moving collector (the only thing that can is arenas, which are user-friendly only in Zig). > We are usi…
CPU utilization is a red herring. Unless you're doing heavy number crunching (which these days heavily favors GPUs) the practical bottleneck on CPU utilization for large general purpose programs (especially when spanning multiple cores) is memory bandwidth. And moving GC is terrible for memory bandwidth compared to both Go-style concurrent GC (which doesn't have to do bulk moves) and manual memory management.
This is not true. The whole point of the algorithm - the reason it was designed - is that the amount of moving is well below what's required in a non-moving collector. The downside is that the algorithm is more complicated and requires an FFI layer for FFI, but even though non-moving collectors are far simpler to implement, every language/runtime that can use moving collectors uses them (and all of those can also use non-moving collectors, too, as Java did earlier on; concurrent mark-and-sweep collectors like Go's or Java's old CMS are easier to make). Whatever you say about the complexity of moving collectors or their impact to latency before the recent invention of pauseless moving collectors, they are widely recognised fact that as the most efficient general purpose memory management solution (but also the most elaborate).
You could argue about certain workloads, but it is ridiculous to claim that the world's top memory management researchers worked for years to come up with an algorithm to be more efficient than mark-and-sweep collectors and malloc/free failed to notice that it has to move objects around a lot (the whole point of the algorithm is that it does not), and then every language that can use the algorithm chooses to use it because they also failed to notice that the algorithm that is so much more costly to implement is so obviously worse.
BTW, Go's reason for using a simpler, older style mark-and-sweep collector isn't that it's better (Google's larger V8 team opted for a moving collector), but that Go can get away with a simpler, less efficient GC because the allocation rate is lower (and we can argue over that, but at least that would be an argument over something that could actually be controversial).
Anyway, if you're interested to know how moving collectors really work, and how they were created to be more efficient than any non-moving general memory management strategy, I go through the basics in a recent talk I gave: https://youtu.be/xr73mR7ii9M
Re: Java 27
#374Earlier quoted context omitted.
> I use Java every day but just to point out that your info about Go‘s GC seems out of date. I'm well aware that Go's GC has improved, but the moving algorithm was designed not just to be fast for a GC, but to be faster than no GC. So Go's new GC is good - for a mark and sweep collector. But it can't compete with a moving collector (the only thing that can is arenas, which are user-friendly only in Zig). > We are usi…
A couple data points, I like Java but I've seen metrics of container fleets at multiple companies that were memory constrained with low CPU usage sitting around underutilized. The reason in both cases was a bunch of memory-heavy yet CPU-efficient Java processes.
The amount of memory a Java program uses is whatever the setting is, not how much it "needs", because the need depends on the preference of the CPU/RAM tradeoff. But again, not many understand that, so we're making that automatic.
Re: Java 27
#375Serious question: when should one use Java for greenfield projects in 2026?
These days, Java is mostly used in greenfield software that has to be very reliable, very performant, and last for many years. So it's often the first choice for banking, telecom, finance, government, defence, manufacturing control, logistics and shipping, media streaming, retail, hospitality, healthcare etc.. It's usually not a first pick for more exciting software, such as Python type checkers, JS bundlers, or TUI…
if you gonna work in a big team or need a project with lots of devs then yeah go for the JVM.
but if you're doing things on the smaller / small scale side. - just use JS/TS or python. you benefit from cheap runtimes such as Cloudflare workers.
Re: Java 27
#376Earlier quoted context omitted.
> However, such optimization is theoretically possible. The stack is definitely faster than anything else. What you're describing isn't a stack, but an automatic arena, and this optimisation is easier to do in Java. It's easier to do in Java because it requires setting a "current arena" or inlining, both of which Java can do more easily, and then either the arena will be heap allocated (which will be slower in Rust)…
> Assembly (which is faster than anything in the same sense: for any program in any language, there exists and Assembly program that's at least as fast) At least you aren't claiming that the JVM is ~1.5 faster than perfectly written assembly :) I disagree with a lot of what you’re writing. However, we’ve reached the point where we need to run benchmarks and analyze the generated code (this is easy to do for compiled…
Yes, and the important point is that when it comes to knowing things statically, abstraction and optimisation are in conflict. The whole point of abstraction is that the implementation details aren't known. So in C++ we always suffer from this problem called "zero overhead abstractions" or "abstraction costs", which means that to give the compiler the information it needs, we have to use less general abstractions, which are viral and harm evolution. What a JIT does is allow the compiler to learn the very things that abstraction hides; yes, it's a virtual call, yes, it could target anything, but I've seen it hit the same target 1000 out of the last 1000 times, so I speculate that this will continue and I'll inline even though I could be wrong.
> The same applies to the GC: the compiler can perform more optimizations when it knows when memory needs to be cleared
I understand why this could be true in theory, but in practice the problem is:
1. not that the compiler knows when an object is unreachable, but that the generated code has to do something at that point, and
2. the most efficient known memory management algorithms - moving collectors and arenas, both work in nearly the same way - are entirely predicated on freeing memory in bulk and on not doing anything when an object becomes unreachable, and so the knowledge of when an object becomes unreachable doesn't help them.
So it is true that C and C++ and Rust always statically know when an object is dead, and you could say that hypothetically they don't need to do anything with that information, but in practice they all act on that information immediately and that's inefficient.
> There remain a small number of cases, such as `switch` statements - where one branch executes 99% of the time, while the other 99 branches execute only 1% of the time.
So the main practical benefit of a JIT isn't that at all, but that it can do the "mother of all optimisations" - inlining - far more aggressively. Inlining is important because it cracks open the abstraction boundary of the inlined subroutine, and allows the compiler to further specialise and optimise things, now with the appropriate context.
Anyway, all of these fundamental questions and differences between languages with more statically known information and figuring out "unprovable" information in practice were very well known before the JVM was built to address the performance problems we had suffered from in large C++ programs. So we can argue over which workloads are helped by this and which aren't, but there is no way to say which is usually faster in the absract (because, again, these considerations were known and taken into account). It's merely an empirical question, and not one that's easy to settle. After more than 25 years of working with C++ and almost 20 years of working with Java, my default is that low-level wins on performance (if written by experts) in smaller programs, and Java wins on performance in larger programs, but of course, there are many caveats in either direction.
Re: Java 27
#377Earlier quoted context omitted.
Most engineers don't get to decide to use a language either. Usually someone with the clout to pick a language has the clout to set style requirements too.
They do get to decide which jobs they take. And the languages involved are one of the easiest filters. A lot easier than checking whether the code a company actually writes is any good. Theoretically you don’t need to write AbstractFactoryProvider in Java, but looking at languages mentioned in job offers, I have a pretty good idea of which of them have a high probability of working with such code and which do not, ev…
For concrete example, it took me a long time to find a job last year due to only fully remote being viable since my wife's autoimmune condition means I'd be risking her health by commuting, and nowadays most places seem to either expect hybrid if you live near an office (I'm within the geographic limits of NYC despite being nowhere near Manhattan), restrict by time zone (there were quite a few jobs I was interested in where they only would accept remote with Pacific or Mountain Time), or have onerous travel requirements (multiple opportunities I interviewed for didn't work out because they expected me to fly to the west coast every couple of months, which between the time there and jet lag would mean I'm not productive close to a quarter of the time).
I was in a fortunate position to be able to hold out for a while and ended up finding a fully job with my preferred language after around eight months, but I had already come up with a timeline for when I should start relaxing certain constraints if it went on longer. Programming language was literally the first constraint that I was going to drop if it lasted a few more months because prioritizing my wife's health is non-negotiable, and I'd rather work in a language I don't like as much on something that I don't feel is actively making the world a worse place than work in my favorite language on adtech or at some cryptocurrency startup. It's not clear to me why it would be a problem for me to care about using my non-favorite programming language well if I happened to be employed to write it.
Re: Java 27
#378Earlier quoted context omitted.
> And thousands of manual get/set functions don't? By definition, they don't. They are verbose and hard to maintain, but if ever in the future you would want to keep the same API surface but change the internal implementation detail, they let you. > But it's not synchronous code, is it? It's easily dozens of lines wrangling Futures, and Thread initialisers, and Executors, and... No, it's done under the hood by the JV…
> but if ever in the future you would want to keep the same API surface but change the internal implementation detail, they let you. So do properties in C# which object initialization relies on. With significantly less manual code, or the need for tedious builder chains and withers. `{ prop = x }` is no more encapsulation breaking than ` .setProp(x) `, but actually makes developer experience better. > No, it's done u…
var x = someFunction()
someOtherFunction()
If you would have written var xTask = SomeFunctionAsync();
await WaitForSomeOtherFunctionAsync();
string x = await xTask;
then it would be: try (var scope = StructuredTaskScope.open()) { // JDK 24+ preview feature
var x = scope.fork(() -> someFunction());
scope.fork(() -> waitForSomeOtherFunction());
scope.join();
String result = x.get(); // already completed
}Re: Java 27
#379Earlier quoted context omitted.
> If someone's asking "why Java" or is saying nulls make it a hard no, then you'd assume that they have a choice in the first place, which generally means they also have some ability to set coding standards at the same time that they're choosing a language. I don't understand that logic. I sometimes ask people to explain why they think a certain policy should be implemented by the government after they state their su…
Of course you assume that; if you're talking about what a policy should be, then you work in a hypothetical world where the policy can be chosen. You don't say "but what about some other minor detail! That would require an additional policy choice, and we can't change related policies." Like if I think my business should open an hour earlier, and you say "but the employees won't be there yet so who will open the door…
Re: Java 27
#380Earlier quoted context omitted.
10 years ago I was working at a place that was building Python systems that had dependency graphs too complicated for pip to handle. I was able to solve the problem for my system with a "wheelhouse" system that could compute a list of wheels that could be installed to build it but the confidence of my team in Python had flagged. I had a sheaf of notes about the problem and figured out the math to build a proper depen…
Before uv came along, pipenv, Poetry, and (much older) Conda all were trying to solve this problem. It's a huge problem for Python that not all languages experience, because Python packages can contain all sorts. At one point if you wanted to install Scipy you had to drag in (and compile, if I remember correctly!) Fortran, of all things[0]. [0] https://stackoverflow.com/a/14822245/61938
I used conda back in that period, it had a correct solver, and it was easy to manage my own packages, but it was slow in the technical sense of "it takes forever to build an environment" and slow in the business sense in that you got something curated which was not always the greatest or the latest but would, back in the day, "just work." Actually you could vendorize any software you need and have your own conda wheels, like I made wheels with different versions of CUDA drivers which are just DLLs so you could be running models with two versions of tensorflow that required two different versions of CUDA and never have to touch the NVIDIA installer.
But today it is a more "just works" experience to use PyPi instead of conda so I don't use conda.
Poetry was a big improvement over pip but I don't believe the resolver was 100% correct (like from looking at the source code) and performance was not that good, not so much because it was written in Python but because it did not have a proper cache, did not exploit concurrency. The Python way would be to use a world class SMT solver for the CPU intensive bit but when the bits hit the bus Rust is better at exploiting concurrency.
So I am happy to have uv.