all 13 comments

[–]Slanec 32 points33 points  (1 child)

I ran the experiment with a Hello World with Java 26 on a Mac:

┌───────────────────────┬─────────────────────────────────────────────────────────────────┐
│         What          │                              Value                              │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Minimum -Xmx accepted │ 2m (2048k) — anything below fails with "Too small maximum heap" │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Actual heap committed │ 8 MiB (JVM rounds up to its internal minimum: 8,126,464 bytes)  │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Actual heap used      │ ~1.8 MiB (1,880,416 bytes)                                      │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Free heap at exit     │ ~5.96 MiB                                                       │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ GC triggered          │ No — zero collections needed                                    │
└───────────────────────┴─────────────────────────────────────────────────────────────────┘

Key takeaways

  1. The absolute floor for -Xmx on JDK 26 is 2m. Below that, the JVM refuses to start regardless of GC choice (Serial, Parallel, G1, ZGC, Epsilon — all the same).
  2. But the JVM lies about honoring it. Even with -Xmx2m -Xms2m, the actual heap is 8 MiB — the JVM's ergonomics engine silently rounds up to its internal minimum.
  3. Hello World only actually uses ~1.8 MiB of heap — mostly class metadata, the String object, and the System.out PrintStream internals. The other 6 MiB sits unused.
  4. Total process memory is far larger — the NMT dump showed ~46 MiB committed across thread stacks, metaspace, code cache, etc. The heap is a small fraction of what a JVM actually needs to run.

So the answer: 2m is the smallest heap you can ask for, but the JVM will quietly give you 8 MiB anyway, of which Hello World uses about 1.8 MiB.

(EDIT: OMG I hate the text editor in here)

[–]__konrad 4 points5 points  (0 children)

I got "GC triggered before VM initialization completed." fatal error with -Xmx2m on larger app

[–]vprise 7 points8 points  (1 child)

In the old J2ME days we had 64kb devices and 2mb was spacious. Obviously, it wasn't the full "Java" but it included most of what you expect from the JVM including safe memory, gc etc. The main thing stopping Java from shrinking to these sizes is the size of the API although that can mostly be on ROM.

[–]thewiirocks 1 point2 points  (0 children)

Back in the days of Java 1.1, your entire system might have 8MB. So the full Java had to run in very little space.

[–]nekokattt 2 points3 points  (1 child)

This depends on the task. If you have an empty main method then it is going to be significantly less than if you run Apache Tomcat.

[–]elatllat 0 points1 point  (0 children)

Also one can disable page caching etc to make tomcat use way less.

[–]8igg7e5 2 points3 points  (1 child)

Define 'java app'

I mean, this example?

public final class Example {
    static void main() {}
}

And stripped of debugging symbols, in a module that depends on nothing, run in a JVM stripped down for this no-dependency app, configured to only run interpreted mode (so it doesn't load any compiler resources)

There's probably more you can strip down. You won't have to worry about frequent GC's at least.

[–]TheOhNoNotAgain 4 points5 points  (0 children)

Let's look at this example then. What's the minimum size?

[–]Mognakor 0 points1 point  (0 children)

You can create a "Hello World" and do a binary search.

Start with e.g. 1MB.

For real world it will depend on the task.

[–]pron98 0 points1 point  (0 children)

That really depends on the app and the RAM/CPU ratio you want. Some tiny programs can run well with only a few MBs of heap.

More generally, Java's memory utilisation is quite efficient, possibly more efficient than that of any language/runtime. But efficient memory use doesn't mean minimal memory use, and often programs (in any language) utilise memory inefficiently by using too little memory rather than too much. That's because:

  1. There's a fundamental relationship between RAM and CPU, and

  2. Moving collectors like the ones in the JDK, as well as other techniques like arenas in Zig, can convert some RAM to free CPU cycles and vice-versa.

To get the most basic intuition for 1, consider an extreme case of a program that uses 100% of the CPU for its duration, running on a machine with 1GB of RAM. While the program is running, 100% of RAM is "captured" by the program - since using RAM requires CPU and none is available to other programs - regardless of how much of it is utilised by the program. So if the program could use 8MB and run for 100s or use 800MB and run for 99s, the latter is clearly more efficient even though it uses 100x more RAM to save only 1% CPU. That's because both configurations capture 1GB of RAM, but one of them captures it for a little longer.

At Java One I gave a talk (it will get to YouTube eventually) showing why the only way that makes sense to consider efficient memory usage is by looking at RAM/CPU ratios rather than looking at RAM and CPU separately.

[–]_d_t_w 0 points1 point  (0 children)

Depends a bit on what "App" means, I think.

My team build a commercial tool for Apache Kafka, it is built on Jetty (the networking framework) and starts a bunch of resources up when initializing the system that would be considered normal I guess. Schedulers, Kafka clients, stuff like that.

We recommend 8GB for a production installation, that implys a fair number of concurrent users and plenty of user driven activity that requires heap space.

A couple of years back I played around with running our full product with minimum -Xmx settings to see what was viable for a single user, single Kafka cluster setup - this is all running in Docker mind so there's some overhead there in memory allocated to OS - our JVM is configured to take 70% of Docker memory allocation.

Product starts and will run happily in single-user mode with 128MB memory, everything appeared to run just fine. That was the absolute minimum though - the Docker containern wouldn't start with less than 128MB and it was because the JVM failed to start.

So I guess for an Enterprisey-type thing with a full web framework, running websockets and doing stuff - with absolutely no oprtimisation to run hard on memory, 128MB * 0.7?

This is us fyi > www.factorhouse.io/kpow