[HN Gopher] Java is fast, code might not be
       ___________________________________________________________________
        
       Java is fast, code might not be
        
       Author : siegers
       Score  : 180 points
       Date   : 2026-03-20 13:43 UTC (9 hours ago)
        
 (HTM) web link (jvogel.me)
 (TXT) w3m dump (jvogel.me)
        
       | koakuma-chan wrote:
       | As much as I love Java, everybody should just be using Rust. That
       | way you are actually in control, know what's going on, etc.
       | Another reason specifically against Java is that the tooling,
       | both Maven and Gradle, still stucks.
        
         | krona wrote:
         | > That way you are actually in control
         | 
         | Programming in Rust is a constant negotiation with the
         | compiler. That isn't necessarily good or bad but I have far
         | more control in Zig, and flexibility in Java.
        
           | koakuma-chan wrote:
           | Yes, there is a learning curve to Rust, but once you get
           | proficient, it no longer bothers you. I think this is more
           | good than bad, because, for example, look at Bun, it is
           | written in Zig, it has so many bugs. They had a bug in their
           | filesystem API that freezed your process, and it stayed
           | unfixed for at least half a year after I filed it. Zig is a
           | nice C replacement, but it doesn't have the same correctness
           | guardrails as Rust.
        
             | krona wrote:
             | Assuming we're talking about the same bug, The filesystem
             | API freeze wasn't caused by Zig's lack of correctness
             | guarantees, but a design flaw in Bun's implementation.
        
               | dryarzeg wrote:
               | Maybe I'm stupid, but I never actually understood people
               | who blame programming languages for bugs in software.
               | Because sure, it's good to have guardrails, but in my
               | opinion, if you're writing a program and there's a bug,
               | unless this bug lies somewhere in implementation of
               | compiler/interpreter/etc, you can't blame the tooling,
               | It's you who introduced this bug. It was your mistake.
               | 
               | It's cool when your tooling warns you about potential
               | bugs or mistakes in implementation, but it's still your
               | responsibility to write the correct code. If you pick up
               | a hammer and hit your finger instead of the nail, then in
               | most cases (though not always) it's your own fault.
        
               | chuckadams wrote:
               | When millions of users constantly make the same mistake
               | with the tool, there may be a problem with the tool,
               | whether it's a defect in the tool or just that it's
               | inappropriate for the job. Blaming the user might give
               | one a righteous feeling, but decade after decade that
               | approach has failed to actually fix any problems.
        
               | dryarzeg wrote:
               | That's why I say "in most cases" - so not always,
               | actually. There might be problems with tools, I'm not
               | trying to deny that. And by the way, what if some (or
               | even most) of the users just don't have enough skill to
               | use the tool properly? Again, there _could be a problem
               | with tool, yes_ , but you can't always blame only tools
               | for mistakes users make.
        
               | koakuma-chan wrote:
               | https://github.com/oven-sh/bun/issues/18192
               | 
               | I am talking about this bug. It looks like it is still
               | unfixed, in the sense, there is a PR fixing it, but it
               | wasn't merged. LOL.
               | 
               | Regardless of whether this specific bug would be caught
               | by Rust compiler, Bun in general is notorious for
               | crashing, just look at how many open issues there are,
               | how many crashes.
               | 
               | Not saying that you cannot make a correct program in Zig,
               | but I prefer having checks that Rust compiler does, to
               | not having them.
        
         | j-vogel wrote:
         | I'm a fan of Rust too. But there are millions of Java
         | applications running in production right now, and some of them
         | are running these anti-patterns today. Not everyone has the
         | option to rewrite in a different language. For those teams,
         | knowing what to look for in a profiler can make a real
         | difference without changing a single dependency.
        
           | koakuma-chan wrote:
           | I think that right now it is easier than ever to rewrite your
           | app in Rust, due to LLMs. Unfortunately there are still
           | people out there who dismiss this idea, and continue having
           | their back-end written in much inferior languages, like
           | JavaScript or Python. If your back-end is written in Java,
           | you aren't even in the worst spot.
        
             | kykat wrote:
             | "You think" is cheap, try doing it, rewrite an existing
             | library in rust and see how it goes. Doing a rough
             | prototype is easy, but the real work starts after that.
        
         | piva00 wrote:
         | Gradle does suck, it gives too much freedom on a tool that
         | should be straightforward and actively design to avoid
         | footguns, it does the opposite by providing a DSL that can
         | create a lot of abstractions to manage dependencies. The only
         | place I worked where the Gradle configuration looked somewhat
         | sane had very strict design guidelines on what was acceptable
         | to be in the Gradle config.
         | 
         | Maven on the other hand, is just plain boring tech that works.
         | There's plenty of documentation on how to use it properly for
         | many different environments/scenarios, it's declarative while
         | enabling plug-ins for bespoke customisations, it has cruft from
         | its legacy but it's quite settled and it just works.
         | 
         | Could Maven be more modern if it was invented now? Yeah, sure,
         | many other package managers were developed since its inception
         | with newer/more polished concepts but it's dependable, well
         | documented, and it just plain works.
        
           | koakuma-chan wrote:
           | I would disagree that either "plain works" because to even
           | package your app into a self-contained .jar, you need a
           | plugin. I can't recall the specifics now, but years ago I
           | spent many hours fighting both Maven and Gradle.
        
             | looperhacks wrote:
             | You "need a plugin" in the sense that every component of
             | maven is a "plugin". The core plugins give you everything
             | you need to build a self-contained jar - if you wanted to,
             | you don't even have to configure the plugins, if you want
             | to write a long cli command instead.
        
             | piva00 wrote:
             | Well, yes? It's a feature provided by a plugin, like any
             | other feature in Maven, you declare the plugin for creating
             | a fat-jar or single-jar and use that. It's just some lines
             | of XML configuration so it plain works.
             | 
             | Like I said, it's not hypermodern with batteries included,
             | and streamlined for what became more common workflows after
             | it was created but it doesn't need workarounds, it's not
             | complicated to define a plugin to be called in one of the
             | steps of the lifecycle, and it's provided as part of its
             | plugin architecture.
             | 
             | I can understand spending many hours fighting Gradle, even
             | I with plenty of experience with Gradle (begrudgingly, I
             | don't like it at all) still end up fighting its idiocies
             | but Maven... It's like any other tool, you need to learn
             | the basics but after that you will only fight it if you are
             | verging away from the well-documented usage (which are
             | plenty, it's been battle-tested for decades).
        
           | computerdork wrote:
           | Just wrote a comment how I've always liked Maven. It's
           | perfect for small and medium sized projects, and for service-
           | oriented architectures/microservices - it seems like it was
           | designed for this! It's main goal is to help you figure out
           | the libraries that you're using and build them in a standard
           | way.
           | 
           | It isn't great for really strange and odd builds, but in that
           | case, you should probably be breaking your project down into
           | smaller components (each with it's own maven file) anyways.
        
         | jayd16 wrote:
         | Not knowing what's going on in Java is a personal problem. The
         | language and jvm have its own quirks but it's no less knowable
         | than any other compiler optimized code. The debugging and
         | introspection tooling in Java is also best in class so I would
         | say it's one of the more understandable run times.
         | 
         | Gradle does suck and maven is ok but a bit ugly.
        
           | dionian wrote:
           | LLMs take the whole argument away. Yes, maven/gradle/sbt suck
           | to work with. But now you can just generate it.
        
             | dionian wrote:
             | I've been using maven for 20+ years, gradle for 10? ant for
             | 5 before that. sbt for 15. I've written custom plugins for
             | all of them. I know them quite well, unfortunately.
             | 
             | I use LLMs to maintain them now. I keep the build files
             | simple. It was an inconvenience before, but a trifle now.
        
             | computerdork wrote:
             | Actually, I like Maven. It's perfect for code that is
             | broken into medium-sized projects, which makes it great for
             | service-oriented architectures (would have said
             | microservices here instead, but think we're learning that
             | breaking our services too finely down is generally not a
             | good idea).
             | 
             | Yeah, it seems like Maven is designed to build just one
             | project with relatively little build-code (although,
             | figuring out versioning of the libs used in your build can
             | get tricky, but guessing this is how it is in most
             | languages). It's still one of my favorites build tools for
             | many situations.
        
           | ActorNightly wrote:
           | Lets look at Java in modern day.
           | 
           | * Most mature Java project has moved to Kotlin.
           | 
           | * The standard build system uses gradle, which is either
           | groovy or kotlin, which gets compiled to java which then
           | compiles java.
           | 
           | * Log4shell, amongst other vulnerabilities.
           | 
           | * Super slow to adopt features like async execution
           | 
           | * Standard repo usage is terrible.
           | 
           | There is no point in using Java anymore. I don't agree that
           | Rust is a replacement, but between Python, Node, and C/C++
           | extensions to those, you can do everything you need.
        
           | computerdork wrote:
           | Actually, really like maven, it's focus on building in
           | standard way is fantastic (but agreed, it look messy, with
           | all its xml and necessary versioning).
        
         | shermantanktop wrote:
         | I'll never understand the impulse to tell the entire world what
         | to do based on your own personal preferences and narrow
         | experiences.
         | 
         | It gets a reaction, though, so great for social media.
        
         | pjmlp wrote:
         | Rust has no place other than deployment scenarios where any
         | kind of automatic resource management, be it tracing GC or
         | reference counting, is not wanted for, either due to technical
         | reasons, or being a waste of time trying to change people's
         | mindset.
        
       | bearjaws wrote:
       | JavaScript can be fast too, it's just the ecosystem and decisions
       | devs make that slow it down.
       | 
       | Same for Java, I have yet to in my entire career see enterprise
       | Java be performant and not memory intensive.
       | 
       | At the end of the day, if you care about performance at the app
       | layer, you will use a language better suited to that.
        
         | j-vogel wrote:
         | Fair point on ecosystem decisions, that's basically the thesis
         | of the post. These patterns aren't Java being slow, they're
         | developers (myself included) writing code that looks fine but
         | works against the JVM. Enterprise Java gets a bad rap partly
         | because these patterns compound silently across large codebases
         | and nobody profiles until something breaks.
        
         | maccard wrote:
         | My experience with the defaults in JavaScript is that they're
         | pretty slow. It's really, really easy to hit the limits of an
         | express app and for those limits to be in your app code. I've
         | worked on JVM backed apps and they're memory hungry (well, they
         | require a reallocation for the JVM) and they're slow to boot
         | but once they're going they are absolutely ripping fast and
         | your far more likely to be bottlenecked by your DB long before
         | you need to start doing any horizontal scaling.
        
           | wiradikusuma wrote:
           | Compile it to native (GraalVM) and you can get it fast while
           | consuming less memory. But now your build is slow :)
        
             | maccard wrote:
             | The minute a project has maven in it the build is slow.
             | Don't even get me started on Gradle...
        
         | FatherOfCurses wrote:
         | "Enterprise Java"
         | 
         | Factories! Factories everywhere!
        
           | wood_spirit wrote:
           | Yes! Obligatory link to the seminal work on the subject:
           | 
           | https://gwern.net/doc/cs/2005-09-30-smith-
           | whyihateframeworks...
        
           | whattheheckheck wrote:
           | Why do you think this plays out over and over again? What's
           | the causal mechanisms of this strange attractor
        
         | pron wrote:
         | Well, JS is fast and Go is faster, but Java is C++-fast.
        
       | kyrra wrote:
       | First request latency also can really suck in Java before
       | hotpathed code gets through the C2 compiler. You can warm up
       | hotpaths by running that code during startup, but it's really
       | annoying having to do that. Using C++, Go, or Rust gets you
       | around that problem without having to jump through the hoops of
       | code path warmup.
       | 
       | I wish Java had a proper compiler.
        
         | bombcar wrote:
         | Do none of the JVMs do that? GraalVM?
        
           | user3939382 wrote:
           | My architecture builds a command registry in Clojure/JVM
           | which runs as a daemon, the registry is shared by a
           | dynamically generated babashka (GraalVM) shell that only
           | includes whitelisted commands for that user. So for the user,
           | unauthorized commands don't even exist, and I get my JVM app
           | with no startup overhead.
        
           | rileymichael wrote:
           | the best way is via CRaC (https://docs.azul.com/crac/) but
           | only a few vendors support it and there's a bit of process to
           | get it setup.
           | 
           | in practice, for web applications exposing some sort of
           | `WarmupTask` abstraction in your service chassis that devs
           | can implement will get you quite far. just delay serving
           | traffic on new deployments until all tasks complete. that way
           | users will never hit a cold node
        
           | pjmlp wrote:
           | They do, to add to another comment of mine elsewhere, JIT
           | caches go all the way back to products like JRockit, and IBM
           | JVM has and it for years in Maestro, now available as OpenJ9.
           | 
           | Too many folks have this mindset there is only one JVM, when
           | that has never been the case since the 2000's, after Java for
           | various reasons started poping everywhere.
        
         | a-dub wrote:
         | i'd be curious about a head to head comparison of how much the
         | c2 actually buys over a static aot compilation with something
         | serious like llvm.
         | 
         | if it is valuable, i'd be surprised you can't freeze/resume the
         | state and use it for instantaneous workload optimized startup.
        
         | looperhacks wrote:
         | You can create a native executable with GraalVM. Alternatively,
         | if you want to keep the JVM: With the ongoing project Leyden,
         | you can already "pre-train" some parts of the JVM warm-up, with
         | full AoT code compilation coming some time in the future.
        
           | senkora wrote:
           | And going the other direction, if you want your C++ binaries
           | to benefit from statistics about how to optimize the steady-
           | state behavior of a long-running process, the analogous
           | technique is profile-guided optimization (PGO).
        
           | Thaxll wrote:
           | GraalVM has a lot of limitations, some popular lib don't work
           | with it. From what I remember anything using reflection is
           | painful to use.
        
           | vbezhenar wrote:
           | GraalVM is terrible. Eats gigabytes of memory to compile
           | super simple application. Spends minutes doing that. If you
           | need compiled native app, just use Golang.
        
             | brabel wrote:
             | I used to be really excited about GraalVM but this,
             | together with limitations in what Java code can run
             | (reflection must be whitelisted - i.e. pain) made me run
             | away from it. I do use Go, but my favourite substitute for
             | Java is actually Dart. It can run as a script, compile to a
             | binary or to a multiplatform "fast" format (a bit like a
             | jar), and performance wise it's par on par with Java! It's
             | faster on some things, a bit slower on other... but in
             | general, compiling to exe makes it extremely fast to start,
             | like Go. I think it even shares some Go binary creation
             | tooling since both are made by Google and I remember when
             | they were implementing the native compiler, they mentioned
             | something about that.
        
         | dionian wrote:
         | This is why I use java for long running processes, if i care
         | about a small binary that launches fast, i just use something
         | slower at runtime but faster at startup like python.
        
           | AlotOfReading wrote:
           | And then you get applications choosing the worst of both
           | worlds, like bazel/blaze.
        
           | packetlost wrote:
           | Python startup time can be pretty abysmal too if you have a
           | lot of imports.
        
             | ptx wrote:
             | This is addressed by PEP 810 (explicit lazy imports) in
             | Python 3.15 (currently in alpha):
             | https://peps.python.org/pep-0810/
        
               | packetlost wrote:
               | Yeah, but it requires code changes to matter
        
           | cogman10 wrote:
           | So long as you aren't in a docker container, The openjdk can
           | do fast startup pretty trivially.
           | 
           | There are options to turn on which cause the JVM to save off
           | and reload compiled classes. It pretty massively improves
           | performance.
           | 
           | You can get even faster if you do that plus doing a jlink
           | jvm. But that's more of a pain. The AOT cache is a lot
           | simpler to do.
           | 
           | https://openjdk.org/jeps/514
        
         | belfthrow wrote:
         | I really hate how completely clueless people on hn are about
         | java. This is not, and has not been an issue for many many
         | years in Java and even the most junior of developers know how
         | to avoid it. But oh no, go and rust is alwaayssss the solution
         | sure.
        
           | bombcar wrote:
           | Ah, but let's port rust to the JVM!
        
           | pythonaut_16 wrote:
           | Can you provide any examples or evidence of Java apps that
           | prove this?
           | 
           | Because in my experience as of 2026, Java programs are
           | consistently among the most painful or unpleasant to interact
           | with.
        
             | belfthrow wrote:
             | Crac / aot cache / ready now all can address this. Not even
             | considering native aot. Multiple low latency trading
             | systems across market markers, hedge funds and ibs prove
             | this. But people just want to compare it to building a cli
             | tool in go or rust.
        
               | pythonaut_16 wrote:
               | But like can you provide an actual example of an
               | application?
               | 
               | > But people just want to compare it to building a cli
               | tool in go or rust.
               | 
               | This seems like the key. HN is definitely biased towards
               | simpler, smaller tools. (And that's not a bad thing!).
               | The most compelling JVM stories I hear are all from much
               | larger scale enterprise settings.
               | 
               | Kafka being a good example. It's very good at what it
               | does, but painful to manage and usually not worth the
               | pain for anyone who's not in a mega enterprise.
        
               | belfthrow wrote:
               | Because in real life, real world applications software is
               | large, long running and needs to be bulletproof. Clis are
               | not powering the world's infrastructure via piped bash
               | scripts. It really baffles me what people actually do as
               | software engineers on here with some of the nonsense that
               | gets thrown around.
        
             | dionian wrote:
             | IntelliJ IDEA is reasonably fast, but of course its hard to
             | make a big desktop app in java be fast.
        
         | pjmlp wrote:
         | Excelsior JET, now gone, but only because GraalVM and OpenJ9
         | exist now.
         | 
         | The folks on embedded get to play with PTC and Aicas.
         | 
         | Android, even if not proper Java, has dex2oat.
        
         | pron wrote:
         | You mostly need a recent JDK. Leyden has already cut down
         | warmup by _a lot_ and is expected to continue driving it down.
         | 
         | https://foojay.io/today/how-is-leyden-improving-java-perform...
         | 
         | https://quarkus.io/blog/leyden-1/
        
         | taeric wrote:
         | I challenge the idea that first request latency is bottle
         | necked by language choice. I can see how that is plausible,
         | mind. Is it a concern for the vast majority of developers?
        
         | bob1029 wrote:
         | AOT is nice for startup time, but there are tradeoffs in the
         | other direction for long tail performance issues in production.
         | 
         | There are JITs that use _dynamic_ profile guided optimization
         | which can adjust the emitted binary at runtime to adapt to the
         | real world workload. You do not need to have a profile ahead of
         | time like with ordinary PGO. Java doesn 't have this yet
         | (afaik), but .NET does and it's a huge deal for things like
         | large scale web applications.
         | 
         | https://devblogs.microsoft.com/dotnet/bing-on-dotnet-8-the-i...
        
         | titzer wrote:
         | I worked on JVMs long ago (almost twenty years now). At that
         | time most Java usage was for long-running servers. The runtime
         | team staunchly refused to implement AOT caching for as long as
         | possible. This was a huge missed opportunity for Java, as
         | client startup time has always, always, always sucked. Only in
         | the past 3-5 years does it seem like things have started to
         | shift, in part due to the push for Graal native image.
         | 
         | I long ago concluded that Java was not a client or systems
         | programming language because of the implementation priorities
         | of the JVM maintainers. Note that I say _priorities_ --they are
         | extremely bright and capable engineers that focus on different
         | use cases, and there isn't much money to be made from a client
         | ecosystem.
        
         | hrmtst93837 wrote:
         | Gaming the JIT just to get startup times in line is a decent
         | sign that Java's "fast" comes with invisible asterisks all over
         | prod graphs. At some point you're managing the runtime, not the
         | app.
         | 
         | AOT options like GraalVM Native Image can help cold starts a
         | lot, but then half your favorite frameworks breaks and you
         | trade one set of hoops for another. Pick which pain you want.
        
       | tripple6 wrote:
       | Do good, don't do bad. Okay.
        
         | abound wrote:
         | I don't think that's a charitable take of the article. To many
         | programmers, it wouldn't be obvious that some of these footguns
         | (autoboxing, string concatenation, etc) are "bad", or what the
         | "good" alternatives are (primitives, StringBuilder, etc).
         | 
         | That said, the article does have the "LLM stank" on it, which
         | is always offputting, but the content itself seems solid.
        
       | liampulles wrote:
       | Understanding algorithmic complexity (in particular, avoiding
       | rework in loops), is useful in any language, and is sage advice.
       | 
       | In practice though, for most enterprise web services, a lot of
       | real world performance comes down to how efficiently you are
       | calling external services (including the database). Just
       | converting a loop of queries into bulk ones can help loads (and
       | then tweaking the query to make good use of indexes, doing
       | upserts, removing unneeded data, etc.)
       | 
       | I'm hopeful that improvements in LLMs mean we can ditch ORMs
       | (under the guise that they are quicker to write queries and the
       | inbetween mapping code with) and instead make good use of SQL to
       | harness the powers that modern databases provide.
        
         | j-vogel wrote:
         | Author here. DB and external service calls are often the
         | biggest wins, thanks for calling that out.
         | 
         | In my demo app, the CPU hotspots were entirely in application
         | code, not I/O wait. And across a fleet, even "smaller" gains in
         | CPU and heap compound into real cost and throughput
         | differences. They're different problems, but your point is
         | valid. Goal here is to get more folks thinking about other
         | aspects of performance especially when the software is running
         | at scale.
        
           | PathOfEclipse wrote:
           | My experience profiling is that I/O wait is never the
           | problem. However, the app may actually be spending most of
           | it's CPU time interacting with database. In general, networks
           | have gotten so fast relative to CPU that the CPU cost of
           | marshalling or serializing data across a protocol ends up
           | being the limiting factor. I got a major speedup once just by
           | updating the JSON serialization library an app used.
        
         | cogman10 wrote:
         | Easy to get wrong as well.
         | 
         | There's a balance with a DB. Doing 1 or 2 row queries 1000
         | times is obviously inefficient, but making a 1M row query can
         | have it's own set of problems all the same (even if you need
         | that 1M).
         | 
         | It'll depend on the hardware, but you really want to make sure
         | that anything you do with a DB allows for other instances of
         | your application a chance to also interact with the DB. Nothing
         | worse than finding out the 2 row insert is being blocked by a
         | million row read for 20 seconds.
         | 
         | There's also a question of when you should and shouldn't join
         | data. It's not always a black and white "just let the DB handle
         | it". Sometimes the better route to go down is to make 2 queries
         | rather than joining, particularly if it's something where the
         | main table pulls in 1000 rows with only 10 unique rows pulled
         | from the subtable. Of course, this all depends on how wide
         | these things are as well.
         | 
         | But 100% agree, ORMs are the worst way to handle all these
         | things. They very rarely do the right thing out of the box and
         | to make them fast you ultimately end up needing to comprehend
         | the SQL they are emitting in the first place and potentially
         | you end up writing custom SQL anyways.
        
           | philipwhiuk wrote:
           | ORMs are a caching layer for dev time.
           | 
           | They store up conserved programming time and then spend it
           | all at once when you hit the edge case.
           | 
           |  _If_ you never hit the case, it 's great. As soon as you do,
           | it's all returned with interest :)
        
             | xigoi wrote:
             | The question is why we don't have database management
             | systems that integrate tightly with the progmming language.
             | Instead we have to communicate between two different
             | paradigms using a textual language, which is itself
             | inefficient.
        
               | runroader wrote:
               | We tried that in 90's RAD environments like Foxpro and
               | others. If it fits the problem, they were great! If not,
               | it's even worse than with an ORM. They rarely fit today
               | since they were all (or mostly) local-first or even
               | local-only. Scaling was either not possible or pretty
               | difficult.
        
               | ivan_gammel wrote:
               | The answer is simple: model optimized for storage and
               | model designed for processing are two different things.
               | The languages used to describe and query them have to be
               | different.
        
               | ghurtado wrote:
               | > The languages used to describe and query them have to
               | be different.
               | 
               | Absolutely not.
               | 
               |  _That which is asserted without evidence can be
               | dismissed without evidence._
        
               | kerblang wrote:
               | > Absolutely not.
               | 
               | Can also be dismissed without evidence
        
               | Shorel wrote:
               | Because every single database vendor will try to lock
               | down their users to their DBMS.
               | 
               | Oracle is a prime example of this. Stored procedures are
               | the place to put all business logic according to Oracle
               | documentation.
               | 
               | This caused backslash from escaping developers who then
               | declared business logic should never be inside the
               | database. To avoid vendor lock-in.
               | 
               | There's no ideal solution, just tradeoffs.
        
               | cogman10 wrote:
               | > Because every single database vendor will try to lock
               | down their users to their DBMS.
               | 
               | I mean, that already happens. It's quite rare to see
               | someone migrate from one database to another. Even if
               | they stuck to pure SQL for everything, it's still a
               | pretty daunting process as Postgres SQL and MSSQL won't
               | be the same thing.
        
               | ghurtado wrote:
               | > It's quite rare to see someone migrate from one
               | database to another.
               | 
               | I'm not discounting the level of effort involved, but I
               | think the reason you don't see this often is because it
               | is rare that simply changing DBMS systems is beneficial
               | in and of itself.
               | 
               | And even if it was frictionless (ie: if we had discovered
               | ORM Samarkanda), the real choices are so limited that
               | even if you did it regularly, you would soon run out of
               | DBMSs to try.
        
               | mike_hearn wrote:
               | https://permazen.io/ exists and is a simpler yet still
               | very powerful way to think about databases (for java but
               | the concepts are general).
               | 
               |  _But_ it 's only really efficient if it can run code
               | right next to the data via fast access - ideally the same
               | machine. The moment you have a DB running on separate
               | hardware or far away from the client, it's going to be
               | slower.
               | 
               | SQL is a very compact way to communicate what you want
               | from a complex database in a way that can be statically
               | analyzed and dynamically optimized. It's also
               | sandboxable. Not so easily to replace.
        
           | liampulles wrote:
           | I agree with you fully yes. One has to watch out for
           | overwhelmingly large or locking queries.
        
         | philipwhiuk wrote:
         | > external services (including the database)
         | 
         | Or even the local filesystem :)
         | 
         | CPU calls are cheap, memory is pretty cheap, disk is bad,
         | spinning disk is very bad, network is 'good luck'.
         | 
         | You can O(pretty bad) most of the time as long as you stay
         | within the right category of those.
        
         | sigbottle wrote:
         | > Understanding algorithmic complexity (in particular, avoiding
         | rework in loops), is useful in any language, and is sage
         | advice.
         | 
         | I recently fixed a treesitter perf issue (for myself) in neovim
         | by just dfsing down the parse tree instead of what most
         | textobject plugins do, which is:
         | 
         | -> walk the entire tree for all subtrees that match this
         | metadata
         | 
         | -> now you have a list of matching subtrees, iterate through
         | said subtree nodes, and see which ones are "close" to your
         | cursor.
         | 
         | But in neovim, when I type "daf", I usually just want to delete
         | the function right under my cursor. So you can just implement
         | the same algorithm by just... dfsing down the parse tree (which
         | has line numbers embedded per nodes) and detecting the matches
         | yourself.
         | 
         | In school, when I did competitive programming and TCS, these
         | gains often came from super clever invariants that you would
         | just sit there for hours, days, weeks, just mulling it over.
         | Then suddenly realize how to do it more cleverly and the entire
         | problem falls away (and a bunch of smart people praise you for
         | being smart :D). This was not one of them - it was just, "go
         | bypass the API and do it faster, but possibly less
         | maintainably".
         | 
         | In industry, it's often trying to manage the tradeoff between
         | readability, maintainability, etc. I'm _very_ much happy to
         | just use some dumb n^2 pattern for n  <= 10 in some loop that I
         | don't really care much about, rather than start pulling out
         | some clever state manipulation that could lead to pretty
         | "menial" issues such as:
         | 
         | - accidental mutable variables and duplicating / reusing them
         | later in the code
         | 
         | - when I look back in a week, "What the hell am I doing here?"
         | 
         | - or just tricky logic in general
         | 
         | I only noticed the treesitter textobject issue because I
         | genuinely started working with 1MB autogen C files at work.
         | So... yeah...
         | 
         | I _could_ go and bug the maintainers to expose a  "query over
         | text range* API (they only have query, and node text range
         | separately, I believe. At least of the minimal research I have
         | done; I haven't kept up to date with it). But now that ties
         | into considerations far beyond myself - does this expose state
         | in a way that isn't intuitive? Are we adding _composable
         | primitives_ or just ad hoc adding features into the library to
         | make it faster because of the tighter coupling? etc. etc.
         | 
         | I used to think of all of that as just kind of "bs accidentals"
         | and "why shouldn't we just be able to write the best algorithms
         | possible". As a maintainer of some systems now... nah, the
         | architectural design is sometimes more fun!
         | 
         | I may not have these super clever flashes of insight anymore
         | but I feel like my horizons have broadened (though part of it
         | is because GPT Pro started 1 shotting my favorite competitive
         | programming problems circa late 2025 D: )
        
           | liampulles wrote:
           | You are not wrong. There are of course tradeoffs here. There
           | are various things that can improve web service performance,
           | but if we are talking about the performance of a web service
           | in comparison to other more general concerns, like
           | maintainability, then I agree trying to make small
           | performance wins falls pretty low on the list.
           | 
           | After all, even if one has some slow and beastly, unoptimized
           | Spring Boot container that chews through RAM, its not that
           | expenseive (in the grand scheme of things) to just replicate
           | more instances of it.
        
         | Seattle3503 wrote:
         | > I'm hopeful that improvements in LLMs mean we can ditch ORMs
         | (under the guise that they are quicker to write queries and the
         | inbetween mapping code with) and instead make good use of SQL
         | to harness the powers that modern databases provide.
         | 
         | Maybe we can ditch active models like those we see in
         | sqlalchemy, but the typed query builders that come with ORMs
         | are going to become more important, not less. Leveraging the
         | compiler to catch bad queries is a huge win.
        
           | liampulles wrote:
           | I use Ecto with Elixir in my day job, and it has a pretty
           | good query building type solution. BUT: I still regularly
           | come into issues where I have to use a fragment in order to
           | do the specific SQL operation that I want, or I start my app
           | and it turns out it has not caught the issue with my query
           | (relating to my specific MySQL version or whatever). Which
           | unfortunately defeats the purpose.
           | 
           | My experience with something like the latest Claude Code
           | models these days has been that they are pretty good at SQL.
           | I think some combination of LLM review of SQL code with smoke
           | tests would do the trick here.
        
         | ackfoobar wrote:
         | > ditch ORMs ... make good use of SQL
         | 
         | I think Java (or other JVM languages) are then best positioned,
         | because of jooq. Still the best SQL generation library I've
         | used.
        
           | vincnetas wrote:
           | thumbs up for jooq
        
           | matwood wrote:
           | Anytime I use a language other than Java it's always jooq
           | that I miss. It's that good.
        
         | laughing_man wrote:
         | I've always found ORMs to be performance killers. It always
         | worked out better to write the SQL directly. The idea that you
         | should have a one-to-one correspondence between your data
         | objects and your database objects is disastrous unless your
         | data storage is trivial.
        
           | chopin wrote:
           | I worked with ORM (EclipseLink) and used SQL just fine.
           | 
           | When using JDBC I found myself quickly in implementing a poor
           | mans ORM.
        
             | roegerle wrote:
             | EclipseLink never received enough love.
        
             | nitwit005 wrote:
             | The issue of creating a DB wrapper doesn't go away by using
             | an ORM. One of the complaints about ORMs I have, in
             | practice, is people often create another wrapper around it.
        
         | matwood wrote:
         | > Just converting a loop of queries into bulk ones can help
         | loads
         | 
         | This is usually the first thing I look for when someone is
         | complaining about speed. Developers often miss it because they
         | are developing against a database on their local machine which
         | removes any of the network latency that exists in deployed
         | environments.
        
         | zadikian wrote:
         | Well before LLMs, I already ditched ORMs. What sometimes holds
         | back SQL is not having a convenient way to call it. Statically-
         | typed languages require you to manually set result types unless
         | you use a compile-time query builder, but that's a whole can of
         | worms. Besides that, many client libs aren't so convenient out
         | of the box, so you still need a few of your own helpers.
         | 
         | Also, before jsonb existed, you'd often run into big blobs of
         | properties you don't care to split up into tables. Now it takes
         | some discipline to avoid shoving things into jsonb that
         | shouldn't be.
        
         | fzeindl wrote:
         | > a lot of real world performance comes down to how efficiently
         | you are calling external services (including the database)
         | 
         | Apart from that my experience over the last 20 years was that a
         | lot of performance is lost because of memory allocation (in
         | GCed languages like Java or JavaScript). Removing allocation in
         | hot loops really goes a long way and leads to 10 or 100 fold
         | runtime improvements.
        
           | kykat wrote:
           | This has been the key for me as well, memory allocation in
           | hot paths is usually the first optimization that I look for.
           | It's quite surprising to see how far very inefficient
           | algorithms (time complexity wise) can go as long as no
           | allocations are made.
        
           | gmueckl wrote:
           | This applies to non-GC languages as well. Memory management
           | is slow. Even with manual memory management I have been able
           | to dramatically speed up code simply by modifying how memory
           | is allocated.
           | 
           | Parts of the GC language crowd in particular have come to
           | hold some false optimistic beliefs about how well a GC can
           | handle allocations. Also, Java and C# can sneak in silly heap
           | allocations in the wrong places (e.g. autoboxing). So there
           | is a tendency for programs to overload the GC with avoidable
           | work.
        
             | mrsmrtss wrote:
             | Autoboxing is more a Java problem mainly because of type
             | erasure with generics. C# has "proper" generics and no
             | hidden boxing is occuring there.
        
         | kykat wrote:
         | I've been using sqlx + postgres very successfully with claude
         | in the last couple of months. However, we've been raw dogging
         | MySQL and node.js at work for over a year, and I also used raw
         | SQLite from C++ before that (I am still traumatized by all the
         | pointers, never again), so...
        
       | jandrewrogers wrote:
       | You can write many of the bad examples in the article in any
       | language. It is just far more common to see them in Java code
       | than some other languages.
       | 
       | Java is only fast-ish even on its best day. The more typical
       | performance is much worse because the culture around the language
       | usually doesn't consider performance or efficiency to be a
       | priority. Historically it was even a bit hostile to it.
        
         | steve1977 wrote:
         | Which, to be fair, in many cases is ok. If you just need to
         | churn out LOB apps for worker drones as cheap as possible,
         | performance is probably not the most important factor.
        
         | this_user wrote:
         | Performance is really not Java's issue. Even bad Java code is
         | still substantially faster than the bulk of modern software
         | that is based on technologies like Python or
         | JavaScript/Node.js.
        
       | comrade1234 wrote:
       | Also finding the right garbage collector and settings that works
       | best for your project can help a lot.
        
       | zvqcMMV6Zcr wrote:
       | > Exceptions for Control Flow
       | 
       | This one is so prevalent that JVM has an optimization where it
       | gives up on filling stack for exception, if it was thrown over
       | and over in exact same place.
        
         | j-vogel wrote:
         | Author here. Great callout. That's the
         | -XX:+OmitStackTraceInFastThrow optimization, been around since
         | JDK 5. The C2 compiler detects exceptions thrown repeatedly
         | from the same site and starts reusing a preallocated instance
         | without filling the stack trace. Good for performance, but it
         | makes debugging harder in production since you lose the trace.
         | You can disable it with -XX:-OmitStackTraceInFastThrow if you
         | need the traces back.
        
         | dust-jacket wrote:
         | ah, thank you. Haven't worked in java for a bit now, but that
         | was the only one I read where I was like "I'm sure we didn't
         | have to avoid this when I worked on java".
         | 
         | The rest were all very familiar. Well, apart from the new
         | stuff. I think most of my code was running in java 6...
        
       | wood_spirit wrote:
       | A subject close to my heart, I write a lot of heavily optimised
       | code including a lot of hot data pipelines in Java.
       | 
       | And aside from algorithms, it usually comes down to avoiding
       | memory allocations.
       | 
       | I have my go-to zero-alloc grpc and parquet and json and time
       | libs etc and they make everything fast.
       | 
       | It's mostly how idiomatic Java uses objects for everything that
       | makes it slow overall.
       | 
       | But eventually after making a JVM app that keeps data in
       | something like data frames etc and feels a long way from J2EE
       | beans you can finally bump up against the limits that only
       | c/c++/rust/etc can get you past.
        
         | polothesecond wrote:
         | > And aside from algorithms, it usually comes down to avoiding
         | memory allocations.
         | 
         | I've heard about HFT people using Java for workloads where
         | micro optimization is needed.
         | 
         | To be frank, I just never understood it. From what I've seen
         | heard/you have to write the code in such a way that makes it
         | look clumsy and incompatible with pretty much any third party
         | dependencies out there.
         | 
         | And at that point, why are you even using Java? Surely you
         | could use C, C++, or any variety of popular or unpopular
         | languages that would be more fitting and ergonomic (sorry but
         | as a language Java just feels inferior to C# even). The biggest
         | swelling point of Java is the ecosystem, and you can't even
         | really use that.
        
           | yunnpp wrote:
           | I am very interested about this and would like an
           | authoritative answer on this. I even went as far as buying
           | some books on code optimization in the context of HFT and I
           | was not impressed. Not a single snippet of assembly; how are
           | you optimizing anything if you don't look at what the
           | compiler produces?
           | 
           | But on Java specifically: every Java object still has a
           | 24-byte overhead. How doesn't that thrash your cache?
           | 
           | The advice on avoiding allocations in Java also results in
           | terrible code. For example, in math libraries, you'll often
           | see void Add(Vector3 a, Vector3 b, Vector3 our) as opposed to
           | the more natural Vector3 Add(Vector3 a, Vector3 b). There you
           | go, function composition goes out the window and the
           | resulting code is garbage to read and write. Not even C is
           | that bad; the compiler will optimize the temporaries away. So
           | you end up with Java that is worse than a low-level
           | imperative language.
           | 
           | And, as far as I know, the best GC for Java still incurs no
           | less than 1ms pauses? I think the stock ones are as bad as
           | 10ms. How anyone does low-latency anything in Java then
           | boggles my mind.
        
             | yrxuthst wrote:
             | Modern ZGC guarantees under 1ms pause times, and Azul's
             | pauseless C4 has been around for a while too.
        
         | dikaflowt wrote:
         | Can you share the libs you 're using?
        
       | taspeotis wrote:
       | Knock Knock
       | 
       | Who's there?
       | 
       |  _long pause_
       | 
       | Java
        
         | ackfoobar wrote:
         | The premise of this joke is dead since 2020, when ZGC was
         | production ready.
        
       | victor106 wrote:
       | this is great, so practical!!!
       | 
       | any other resources like this?
        
       | titzer wrote:
       | For fillInStackTrace, another trick is to define your own
       | Exception subclass and override the method to be empty. I learned
       | this trick 15+ years ago.
       | 
       | It doesn't excuse the "use exceptions for control flow" anti-
       | pattern, but it is a quick patch.
        
         | ivan_gammel wrote:
         | God, please make me unsee it. That's a cool trick that turns
         | into an anti-pattern itself if abused.
        
       | hiyer wrote:
       | I ran into 5 and 7 in a Flink app recently - was parsing a
       | timestamp as a number first and then falling back to iso8601
       | string, which is what it was. The flamegraph showed 10% for the
       | exception handling bit. While fixing that, also found repeated
       | creation of datetimeformatter. Both were not in loops, but both
       | were being done for every event, for 10s of 1000s of events every
       | second.
        
         | wood_spirit wrote:
         | (Perhaps a good library for timestamp code in data pipelines
         | https://github.com/williame/TimeMillis)
        
       | cmovq wrote:
       | When you're using a programming language that naturally steers
       | you to write slow code you can't only blame the programmer.
       | 
       | I was listening to someone say they write fast code in Java by
       | avoiding allocations with a PoolAllocator that would "cache"
       | small objects with poolAllocator.alloc(),
       | poolAllocator.release(). So just manual memory management with
       | extra steps. At that point why not use a better language for the
       | task?
        
         | ablob wrote:
         | You might have an application for which speed is not important
         | most of the time. Only one or two processes might require
         | allocation-free code. For such a case, why would you burden all
         | of the other code with the additional complexity? Calling out
         | to a different language then may come with baggage you'd rather
         | avoid.
         | 
         | A project might also grow into these requirements. I can easily
         | imagine that something wasn't problematic for a long time but
         | suddenly emerged as an issue over time. At that point you
         | wouldn't want to migrate the whole codebase to a better
         | language anymore.
        
         | cogman10 wrote:
         | Bad idea. I've made a pool allocator before, but that was for
         | expensive network objects and expensive objects dealing with
         | JNI.
         | 
         | Doing it to avoid memory pressure generally means you simply
         | have a bad algorithm that needs to be tweaked. It's very rarely
         | the right solution.
        
           | gf000 wrote:
           | Not sure why you are down voted. Depending on how its used it
           | could actually be detrimental to performance.
           | 
           | The JVM may optimize many short lived objects better than a
           | pool of objects with less reasonably lifetimes.
        
             | cogman10 wrote:
             | This is the second time this week on HN that I've seen
             | people suggesting object pools to solve memory pressure
             | problems.
             | 
             | I generally think it's because people aren't experienced
             | with diagnosing and fixing memory pressure. It's one of the
             | things I do pretty frequently for my day job. I'm fortunate
             | enough to be the "performance" guy at work :).
             | 
             | It'll always depend on what the real issue is, but
             | generally speaking the problem to solve isn't reinventing
             | garbage collection, but rather to eliminate the reason for
             | the allocation.
             | 
             | For example, a pretty common issue I've seen is copying a
             | collection to do transformations. Switching to streams,
             | combining transformation operations, or in an extreme case,
             | I've found passing around a consumer object was the way to
             | avoid a string of collection allocations.
             | 
             | Even the case where small allocations end up killing
             | performance, for example like the autoboxing example of the
             | OP, often the solution is to either make something mutable
             | that isn't, or to switch to primitives (Valhalla can't come
             | soon enough).
             | 
             | Heck, sometimes even an object cache is the right solution.
             | I've had good success reducing the size of objects on the
             | heap by creating things like `Map<String, String>` and then
             | doing a `map.computeIfAbsent(str, Function.identity());`
             | (Yes, I know about string interning, no I don't want these
             | added to the global intern cache).
             | 
             | Regardless, the first step is profiling (JFRs and heap
             | dumps) to see where memory is spent and what is dominating
             | the allocation rate. That's a first step that people often
             | skip and jump straight to fixing what they think is broken.
        
         | ivan_gammel wrote:
         | TBH, I do not see how Java as a language steers anyone to use
         | one those shotguns. E.g. the knowledge about algorithmic
         | complexity is foundational, the StringBuilder is junior-level
         | basic knowledge.
        
           | nightpool wrote:
           | How would you handle validating numeric input in a hot path
           | then? All of the solutions proposed in #5 are incomplete or
           | broken, and it stems from the fact that Java's language
           | design over-uses exceptions for error handling in places
           | where an optional value would be much safer and faster.
        
             | ivan_gammel wrote:
             | Normally in 100% cases, with parseInt/parseDouble etc.
             | Getting NumberFormatException so frequently on a hot path
             | that it impacts performance means, that you aren't solving
             | the parsing number problem, you are solving a guessing type
             | problem, which is out of scope for standard library and
             | requires custom parser.
        
               | nightpool wrote:
               | Okay, but this contradicts your original statement that
               | "Java doesn't steer anyone to use these [footguns]".
               | Every language has a way to parse integers, and most
               | developers do not need a custom parser. Only in Java does
               | that suddenly become a performance footgun.
        
               | ivan_gammel wrote:
               | It does not. If you need to parse a number, you use
               | standard library and you will be fine. The described case
               | with huge impact on hot path is the demonstration why
               | using brains is important. The developer that will get
               | into this mess is the one who will find the way to
               | suffocate his code with performance bottlenecks in
               | thousand other ways. It's not a language or library
               | problem.
        
               | dionian wrote:
               | Yes, parseInt et al work very fast for good inputs. What
               | percentage of your inputs are invalid numbers and why ?
        
               | ivan_gammel wrote:
               | > What percentage of your inputs are invalid numbers and
               | why ?
               | 
               | This is a wrong question to ask in this context. The
               | right question to ask is when actually exceptional flow
               | becomes a performance bottleneck. Because, obviously, in
               | a desktop or even in a server app validating single user
               | input even 99% of wrong inputs won't cause any trouble.
               | It may become a problem with bulk processing, but then,
               | and I have to repeat myself here, it is no longer a
               | number parsing problem, it's a problem of not
               | understanding what your input is.
        
             | kerblang wrote:
             | > Java's language design over-uses exceptions for error
             | handling
             | 
             | No, _library authors '_ design over-uses exceptions. Also
             | refer to people using exceptions to generate 404 http
             | responses in web systems - hey, there's an easy DDOS...
             | This _can_ include some of Java 's standard libraries,
             | although nothing springs to mind.
             | 
             | Exceptions are not meant for mainstream happy-path
             | execution; they mean that something is broken. Countless
             | times I have had to deal garbage-infested logs where one
             | programmer is using exceptions for rudimentary validation
             | and another is dumping the resulting stack traces left and
             | right as if the world is coming to end.
             | 
             | It is a problem, but it's an abuse problem, not a standard
             | usage problem.
        
         | ekkeke wrote:
         | This point gets raised every single time managed languages and
         | low latency development come up together. The trade off is
         | running "fast" all of the time, even when you don't have to, vs
         | running slow most of the time and tinkering when you need to go
         | fast.
         | 
         | I've spent a fair few years developing lowish (10-20us wire to
         | wire) latency trading systems and the majority of the code does
         | not need to go fast. It's just wasted effort, a debugging
         | headache, and technical debt. So the natural trade off is a bit
         | of pain to make the hot path fast through spans, unsafe code,
         | pre-allocated object pools, etc and in return you get to use a
         | safe and easy programming language everywhere else.
         | 
         | In C# low latency dev is not even that painful, as there are a
         | lot of tools available specifically for this purpose by the
         | runtime.
        
         | d_burfoot wrote:
         | > So just manual memory management with extra steps
         | 
         | This is actually the perfect situation: you are allowed to do
         | it carefully and manually for 1% of code on the hot path, but
         | you don't have to worry about it for the 99% of the code that's
         | not.
        
         | laughing_man wrote:
         | Java doesn't steer you into object pools. I wrote Java code for
         | 20 years and never used a cache to avoid allocating objects,
         | and never saw a colleague use one. The person you were talking
         | to doesn't know what he's doing.
        
         | cxr wrote:
         | The problem with comments like these is that guessing what
         | "better language" a commentator has in mind is always an
         | exercise left up to the reader. And that tends to be by design
         | --it's great for potshots and punditry, because it means not
         | having make a concrete commitment to anything that might
         | similarly be confronted and torn apart in the replies--like if
         | the "better language" alluded to is C (and it generally
         | is)--the language where the standard library "steers" you
         | towards quadratic string operations because the default/natural
         | way to refer to a string's length is O(n).
        
         | andai wrote:
         | I decompiled Project Zomboid (written in Java) a while back,
         | because I was curious about the performance issues I was having
         | with the game. (Very laggy on my 10 year old laptop, while
         | looking like The Sims 1.) I figured, best case scenario I find
         | some easy bottlenecks and I can patch in a fix.
         | 
         | Well, the whole thing was standard Java OOP, except they also
         | had a bunch of functional programming stuff on top of that. I
         | can relate to that -- I think they were university students
         | when they started, and I definitely had an OOP and FP phase.
         | But then they just... kept it, 10+ years later.
         | 
         | So while it's true that you can write C in any language...
         | those kind of folks don't tend to use Java in the first place
         | ;)
         | 
         | --
         | 
         | (Except Notch? Well, his code _looks_ like C, not sure if it 's
         | actually fast! I really enjoyed his 4 kilobyte java games back
         | in the day, I think he published the source for each one too.)
         | 
         | EDIT: Found it!
         | 
         | https://web.archive.org/web/20120317121029/http://www.mojang...
         | 
         | Edit 2: This one has a download, still works!
         | 
         | https://web.archive.org/web/20120301015921/http://www.mojang...
        
           | bigwheels wrote:
           | What is the ending of your story!? Did you find and fix some
           | bottlenecks?
        
         | kykat wrote:
         | I saw something like that being suggested when working with GIS
         | data with many points as classes in Java, the object overhead
         | for storing XYZ doubles is quite crazy. The optimization was to
         | build a global double array and use "pointers" to get and set
         | the number in the array.
         | 
         | Even JavaScript is much better for this, much, much better.
        
           | lern_too_spel wrote:
           | You're describing array of structs vs. struct of arrays. Even
           | in JavaScript, you would have to manually do the latter.
        
             | kykat wrote:
             | V8 automatically optimizes objects with the same shape into
             | efficient structs, making array of objects much more
             | efficient than in Java.
             | 
             | And the manually manager int array acts more like system
             | memory, it's not continuous, so you could have point i 0
             | and 2 and the data would be: [1, 2, 3, x, x,x, 3, 2, 1] (3D
             | points).
             | 
             | So I am not describing a struct of arrays.
        
               | spankalee wrote:
               | Hidden class optimizations just make JavaScript objects
               | behave a little more like Java class instances, where the
               | VM knows where to find each field, rather than having to
               | look it up like a map.
               | 
               | It doesn't make JS faster than Java, it makes it almost
               | as fast in some cases.
        
               | kykat wrote:
               | I can only say what I observed in testing, and that's
               | that having millions of instances of a class like
               | Point3D{x, y, z} in JS uses significantly less memory
               | than in Java (this was tested on Android, not sure if
               | relevant). It was quite some time ago so I don't remember
               | the details.
        
           | spankalee wrote:
           | JavaScript has the exact same issue - objects are on the heap
           | and require allocation and pointer dereferencing. For huge
           | collections of numbers, arrays _might_ be better.
           | 
           | But JS has another problem: there's no way to force a number
           | to be unboxed (no primitive vs boxed types), so the array of
           | doubles might very well be an array of pointers to
           | numbers[1].
           | 
           | But with hidden class optimizations an object might be able
           | to store a float directly in a field, so the array of objects
           | will have one box per (x,y,z), while an array of "numbers"
           | might have one box per number, so 3x as many. My guess is,
           | without benchmarking, is that JS is much worse than Java
           | then, because the "optimization" will end up being worse.
           | 
           | [1]: Most JS engines have an optimization for small ints,
           | called SMIs, that use pointer tagging to support either an
           | int or a references, but I don't think they typically do this
           | optimization for floats.
        
         | wiseowise wrote:
         | > At that point why not use a better language for the task?
         | 
         | Such as?
        
       | ww520 wrote:
       | The autoboxing in a loop case can be handled by the compiler.
        
       | kpw94 wrote:
       | The Autoboxing example imo is a case of "Java isn't so fast". Why
       | can't this be optimized behind the scenes by the compiler ?
       | 
       | Rest of advice is great: things compilers can't really catch but
       | a good code reviewer should point out.
        
         | vbezhenar wrote:
         | Why should compiler optimize obviously dumb code? If developer
         | wants to create billions of heap objects, compiler should
         | respect him. Optimizing dumb code is what made C++ unbearable.
         | When you write one code and compilers generates completely
         | different code.
        
           | carlmr wrote:
           | The problem is rather that Java doesn't have generics and
           | structs, so you're kind of forced to box things or can't use
           | collections.
        
         | kllrnohj wrote:
         | javac for better or worse is aggressively against doing
         | optimizations to the point of producing the most ridiculously
         | bad code. The belief tends to be that the JIT will do a better
         | job fixing it if it has byte code that's as close as possible
         | to the original code. But this only helps if a) the code ever
         | gets JIT'd at all (rarely true for eg class initializers), and
         | b) the JIT has the budget to do that optimization. Although
         | JITs have the advantage of runtime information, they are also
         | under immense pressure to produce any optimizations as fast as
         | possible. So they rarely do the level of deep optimizations of
         | an offline compiler.
        
       | cogman10 wrote:
       | Nitpick just because.
       | 
       | Orders by hour could be made faster. The issue with it is it's
       | using a map when an array works both faster and just fine.
       | 
       | On top of that, the map boxes the "hour" which is undesirable.
       | 
       | This is how I'd write it                   long[] ordersByHour =
       | new long[24];         var deafultTimezone =
       | ZoneId.systemDefault();         for (Order order : orders) {
       | int hour = order.timestamp().atZone(deafultTimezone).getHour();
       | ordersByHour[hour]++;         }
       | 
       | If you know the bound of an array, it's not large, and you are
       | directly indexing in it, you really can't do any better
       | performance wise.
       | 
       | It's also not less readable, just less familiar as Java devs
       | don't tend to use arrays that much.
        
         | Okx wrote:
         | maybe it would be a little better to use ints rather than
         | longs, as Java lists can't be bigger than the int max value
         | anyways. Saves you a cache line or two.
        
           | cogman10 wrote:
           | Fair point, but it is possible this isn't a list but rather
           | some sort of iterable. Those can be boundless.
           | 
           | Practically speaking, that would be pretty unusual. I don't
           | think I've ever seen that sort of construct in my day to day
           | coding (which could realistically have more than 1B
           | elements).
        
         | wood_spirit wrote:
         | Also zap the timestamp instant objects if you really need
         | speed; see https://github.com/williame/TimeMillis
        
       | jerf wrote:
       | Any non-trivial program that has never had an optimizer run on it
       | has a minimal-effort 50+% speedup in it.
        
       | Okx wrote:
       | The code:                 public int parseOrDefault(String value,
       | int defaultValue) {           if (value == null ||
       | value.isBlank()) return defaultValue;           for (int i = 0; i
       | < value.length(); i++) {               char c = value.charAt(i);
       | if (i == 0 && c == '-') continue;               if
       | (!Character.isDigit(c)) return defaultValue;           }
       | return Integer.parseInt(value);       }
       | 
       | Is probably worse than Integer.parseInt alone, since it can still
       | throw NumberFormatExceptions for values that overflow (which is
       | no longer handled!). Would maybe fix that. Unfortunately this is
       | a major flaw in the Java standard library; parsing numbers
       | shouldn't throw expensive exceptions.
        
         | deepsun wrote:
         | And it will fail with "-"
        
         | j-vogel wrote:
         | Good catches from several of you. The original fix dropped the
         | try-catch entirely which was a regression for overflow and edge
         | cases like a bare "-". Updated the post to keep a try-catch
         | around the final parseInt as a safety net. The pre-validation
         | still avoids the expensive path for the common cases, which is
         | the core point. Appreciate the feedback.
        
         | zahlman wrote:
         | Not to mention it's extra work in the case where the input
         | _usually is_ valid.
        
       | spankalee wrote:
       | Avoiding Java's string footguns is an interesting problem in
       | programming languages design.
       | 
       | The String.format() problem is most immediately a bad compiler
       | and bad implementation, IMO. It's not difficult to special-case
       | literal strings as the first argument, do parsing at compile
       | time, and pass in a structured representation. The method could
       | also do runtime caching. Even a very small LRU cache would fix a
       | lot of common cases. At the very least they should let you make a
       | formatter from a specific format string and reuse it, like you
       | can with regexes, to explicitly opt into better performance.
       | 
       | But ultimately the string templates proposal should come back and
       | fix this at the language level. Better syntax and guaranteed
       | compile-time construction of the template. The language should
       | help the developer do the fast thing.
       | 
       | String concatenation is a little trickier. In a JIT'ed language
       | you have a lot of options for making a hierarchy of string
       | implementations that optimize different usage patterns, and still
       | be fast - and what you really want for concatenation is a
       | RopeString, like JS VMs have, that simply references the other
       | strings. The issue is that you don't want virtual calls for hot-
       | path string method calls.
       | 
       | Java chose a single final class so all calls are direct. But they
       | should have been able to have a very small sealed class hierarchy
       | where most methods are final and directly callable, and the
       | virtual methods for accessing storage are devirtualized in
       | optimized methods that only ever see one or two classes through a
       | call site.
       | 
       | To me, that's a small complexity cost to make common string
       | patterns fast, instead of requiring StringBuilder.
        
         | LtWorf wrote:
         | Sometimes running strace on jvm software you will see some
         | sycall patterns that are incredibly inefficient.
        
         | brabel wrote:
         | Yeah, Java is pretty fast despite the fact that it still has
         | these kinds of obviously suboptimal things going on.
         | 
         | I love how Zig, D and Rust do exactly what you say: parse the
         | format string at compile time, making it super efficient at
         | runtime (no parsing, no regex, just the optimal code to get the
         | string you need).
         | 
         | I say this but I write most of my code in Java/Kotlin :D . I
         | just wish I could write more low-level languages for super
         | efficient code, but for what I do, Java is more than enough.
        
           | jandrewrogers wrote:
           | > Zig, D and Rust
           | 
           | Also C++, which works the same way.
        
           | arikrahman wrote:
           | I admire Rich Hickey's approach of building on top of the
           | Java ecosystem for this reason, adding a functional first
           | approach with emphasis on data structures, where using the
           | right algorithms comes naturally.
        
           | mike_hearn wrote:
           | Kotlin string interpolation turns into the fancy
           | invokedynamic based string concatenation behind the scenes so
           | it's very optimized: https://openjdk.org/jeps/280
        
         | rf15 wrote:
         | > But ultimately the string templates proposal should come back
         | and fix this at the language level.
         | 
         | They tried, its opponents dilluted it to the point of
         | uselessness and now will forever use this failed attempt as a
         | wedge.
         | 
         | I'm sorry, I don't believe Java will get sensible String
         | templates in our life time.
        
       | uraura wrote:
       | I thought those were common sense until I worked on a program
       | written by my colleague recently.
        
       | Izkata wrote:
       | "Java is slow" is a reputation it earned in the 90s/2000s because
       | the JVM startup (at least on Windows) was extremely slow, like
       | several seconds, with a Java-branded splash screen during that
       | time. Even non-technical people made the association.
        
         | marginalia_nu wrote:
         | Java's start up speeds were greatly improved by the engineers
         | at Oracle, but thankfully we invented springboot and guice to
         | get around that problem.
        
       | spwa4 wrote:
       | Java IS fast. The time between deciding to use Java and Oracle's
       | lawyers breaking down your door is measured in just weeks these
       | days.
        
       | latchkey wrote:
       | When they say that AI will replace programmers, I think of this
       | article and come to terms with my own job security.
       | 
       | Most of this stuff is just central knowledge of the language that
       | you pick up over time. Certainly, AI can also pick this stuff up
       | instantly, but will it always pick the most efficient path when
       | generating code for you?
       | 
       | Probably not, until we get benchmarks into the hot path of our
       | test suite. That is something someone should work on.
        
       | sgbeal wrote:
       | Slight correction:
       | 
       | > StringBuilder works off a single mutable character buffer. One
       | allocation.
       | 
       | It's one allocation to instantiate the builder and _any_ number
       | of allocations after that (noting that it's optimized to reduce
       | allocations, so it's not allocating on every append() unless
       | they're huge).
        
       | seu wrote:
       | I'm a bit surprised to see those examples, because there's
       | nothing really new here. These are typical beginner pitfalls and
       | have been there for at least a decade or more. Or maybe it's
       | because I learned java in the late 90s and later used it for
       | J2ME, and then using things like StringBuilder (StringBuffer in
       | the old days) were almost mandatory, and you would be very
       | careful trying to avoid unnecessary object allocations.
        
         | larsnystrom wrote:
         | I remember writing Java for our introductory programming course
         | at university around 2010. I was already familiar with object
         | oriented programming in PHP at the time, so I just wrote the
         | Java code like I would write PHP. I was absolutely astounded at
         | the poor performance of the Java app. I asked one of our tutors
         | and I can still remember him looking at the code and saying
         | something along the lines of "oh, you're instantiating objects
         | in a loop, that's obviously going to be slow". Like, what? If I
         | can do this performantly in freakin PHP, how can Java, the
         | flagship of OOP, not have fast instantiation of objects? I'm
         | still shaking my head thinking about it.
        
           | zahlman wrote:
           | Did you actually benchmark _that task_ against similarly-
           | architected PHP code?
        
           | lern_too_spel wrote:
           | Your tutor misdiagnosed the issue. These allocations in a
           | tight loop would have used bump allocation on Java 6 in 2010,
           | and the young generation would have used a copy collector,
           | which would have freed those objects more cheaply than any
           | unspecialized malloc/free. It would have beaten the pants off
           | of PHP's reference counting GC.
        
       | layer8 wrote:
       | For #5, the "fix" [0] is incomplete, because you will still get a
       | NumberFormatException when the value is out of range. For _int_ ,
       | you could check if there are more or less than 10 digits, and use
       | parseLong() when there are exactly 10 digits. For _long_ , you
       | can use BigInteger when there are exactly 18 digits. After
       | skipping any leading zeros, of course. Or you could just
       | replicate the JDK's parsing implementation and change the part
       | where it throws NumberFormatException (at the possible cost of
       | foregoing JIT intrinsics).
       | 
       | A second bug is that Character.isDigit() returns true for non-
       | ASCII Unicode digits as well, while Integer.parseInt() only
       | supports ASCII digits.
       | 
       | Another bug is that the code will fail on the input string "-".
       | 
       | Lastly, using value.isBlank() is a pessimization over
       | value.isEmpty() (or just checking value.length(), which is read
       | anyway in the next line), given that the loop would break on the
       | first blank character. It makes the function not be constant-
       | time, along with the first point above that the length of the
       | digit sequence isn't being limited.
       | 
       | [0]                    public int parseOrDefault(String value,
       | int defaultValue) {             if (value == null ||
       | value.isBlank()) return defaultValue;             for (int i = 0;
       | i < value.length(); i++) {                 char c =
       | value.charAt(i);                 if (i == 0 && c == '-')
       | continue;                 if (!Character.isDigit(c)) return
       | defaultValue;             }             return
       | Integer.parseInt(value);         }
        
       | zahlman wrote:
       | > Accidental O(n2) with Streams Inside Loops
       | 
       | Man that code looks awful. Really reminds me of why I drifted
       | away from Java over time. Not just the algorithm, of course; the
       | repetitiveness, the hoops that you have to jump through in order
       | to do pretty "stream processing"... and then it's not even an FP
       | algorithm in the end, either way!
       | 
       | Honestly the only time I can imagine the "process the whole
       | [collection] per iteration" thing coming up is where either you
       | really do need to compare (or at least really are intentionally
       | comparing) each element to each other element, or else this exact
       | problem of building a histogram. And for the latter I honestly
       | haven't seen people fully fall into this trap very often. More
       | commonly people will try to iterate over the possible buckets
       | (here, hour values), sometimes with a first pass to figure out
       | what those might be. That's still extra work, but at least it's
       | O(kn) instead of O(n^2).
       | 
       | You _can_ do this sort of thing in an elegant,  "functional"
       | looking way if you sort the data first and then group it by the
       | same key. That first pass is O(n lg n) if you use a classical
       | sort; making a histogram like this in the first place is
       | basically equivalent to radix sort, but it's nice to not have to
       | write that yourself. I just want to show off what it _can_ look
       | like e.g. in Python:                 def local_hour(order):
       | return datetime.datetime.fromtimestamp(order.timestamp).hour
       | groups = itertools.groupby(sorted(orders, key=local_hour),
       | key=local_hour)       orders_by_hour = {hour: len(list(orders))
       | for (hour, orders) in groups}
       | 
       | Anyway, overall I feel like these kinds of things are mostly done
       | by people who don't need to have the problem explained, who have
       | simply been lazy or careless and simply need to be made to look
       | in the right place to see the problem. Cf. Dan Luu's anecdotes
       | https://danluu.com/algorithms-interviews/ , and I can't seem to
       | find it right now but the story about saving a company millions
       | of dollars finding Java code that was IIRC resizing an array one
       | element at a time.
       | 
       | (Another edit: originally I missed that the code was only trying
       | to count the number of orders in each hour, rather than
       | collecting them. I fixed the code above, but the discussion makes
       | less sense for the simplified problem. In Python we can do this
       | with `collections.Counter`, but it wouldn't be unreasonable to
       | tally things up in a pre-allocated `counts_by_hour = [0] * 24`
       | either.)
       | 
       | ----
       | 
       | Edit:
       | 
       | > String.format() came in last in every category. It has to...
       | StringBuilder was consistently the fastest. The fix: [code not
       | using StringBuilder]... Use String.format() for the numeric
       | formatting where you need it, and let the compiler optimize the
       | rest. Or just use a StringBuilder if you need full control.
       | 
       | Yeah, this is confused in a way that I find fairly typical of LLM
       | output. The attitude towards `String.format` is just plain
       | inconsistent. And there's no acknowledgment of how multiple `+`s
       | in a line get optimized behind the scenes. And the "fix" still
       | uses `String.format` to format the floating-point value, and
       | there's no investigation of what that does to performance or
       | whether it can be avoided.
        
       | EricRiese wrote:
       | This is a Spring specific gripe and I know this blog post doesn't
       | assume Spring, but I hate seeing `new ObjectMapper()`. Spring
       | Boot auto configures an ObjectMapper for you and you probably
       | want the customization it gives you, including `java.time`
       | handling and classpath scanning. I've wrestled with so many bugs
       | caused by not using the `ObjectMapper` bean.
        
         | dionian wrote:
         | well yeah Jackson is slow.
        
         | computerdork wrote:
         | Have always really liked Java, but yeah, Spring overall has
         | been terrible for the language. Autowiring is against the
         | principles of a typesafe programming language - Don't make me
         | guess what what object is going to be attached to a reference.
         | And if you do, at least figure out what linked object is at
         | compile time, not at run time.
         | 
         | Spring autowiring makes Java seem as a whole unnecessarily
         | complex. Think it should be highly discouraged in the language
         | (unless it is revamped and made apart of the compiler).
         | 
         | ... not sure how this applies to the ObjectMapper, as I haven't
         | programmed in Java in awhile. ... and my gripe doesn't apply to
         | SpringBoot though:)
        
       | pregnenolone wrote:
       | Java really isn't fast unless you're basically writing C style
       | Java. Abstracting is expensive in Java and if one can't abstract,
       | what's the point of writing Java in 2026? After all these years
       | value types aka Valhalla are still in a soon(tm) state. If it
       | weren't for Project Loom, which is genuinely awesome, I wouldn't
       | see any justification for using Java in this day and age.
        
         | fnord77 wrote:
         | The world has shown that it doesn't need all those intricate
         | design patterns that java fosters.
         | 
         | It does seem like java missed their chance, it's a shame.
        
         | jimbokun wrote:
         | Compared to what?
        
         | dionian wrote:
         | my hotspot JITd code is quite fast
        
       | zmmmmm wrote:
       | String concatenation in a loop is a 1990's era Java footgun. It's
       | interesting the things that have persisted vs been cast aside.
       | Very significant design decisions have been enforced on far less
       | grounds than the stupidity of how the default String
       | concatenation operator works.
        
         | inglor_cz wrote:
         | This. I learnt Java in 2004-5, when 1.4 was dominant and
         | O'Reilly books were a much better training resource than
         | whatever you found on the Internet.
         | 
         | Concatenating of thousands of individual Strings was already
         | considered a well-known performance killer back then.
         | 
         | Interesting to see this in the wild in 2026.
        
         | IsTom wrote:
         | Does Java not have some kind of helper function to join
         | strings? Why'd anyone write loops like that?
        
       | tombert wrote:
       | I think that the `sychronized` keyword in Java was a mistake.
       | 
       | I've seen classes that are meant to be used by multiple threads
       | where literally every method has `synchronized` because "that was
       | the only way they could get it to work". Of course, if literally
       | every method is synchronized it literally _can 't_ actually be
       | used by multiple threads, it just looks like it is.
       | 
       | Generally speaking I work pretty hard to avoid any kind of locks.
       | Locks can be an anti-pattern in my mind: for a lot of problems,
       | if I am reaching for a lock, it's because I haven't actually
       | thought through the problems well enough. They're a bandaid and
       | they create potential choke-points in the app. I also think that
       | they're a crappy fix to try and shoehorn non-concurrent patterns
       | into a concurrent landscape.
       | 
       | I personally think that making something thread-safe and
       | concurrent while also being maintainable and fast is a hard
       | problem, and I think lazily trying to add threads into concurrent
       | applications is a good way to write terrible code that is
       | impossible to debug.
       | 
       | Obviously no accounting for taste, but when I write programs now,
       | I kind of always make them concurrent-first (generally using
       | and/or reinventing the actor model). I try and build my initial
       | algorithm to accept that concurrency is inevitable and start that
       | from the get go. I can't remember the last time I reached for
       | `synchronized`, though every now and then I do have to reach for
       | ReentrantLock, and I always feel dirty doing so.
        
       ___________________________________________________________________
       (page generated 2026-03-20 23:01 UTC)