It doesn't really need 840mb. If you force a GC after waiting for it to load the stories it needs "only" 350mb. Try "jcmd main GC.run" (if you have any JDK 11+ installed).
The problem is Java sees lots of memory lying unused and figures ... why should I work harder to free it up? It'll waste CPU time and energy and I might need the memory later.
In theory it actually will do background GCs and free memory back to the OS anyway so I'm not sure why it's not happening here; possibly that feature needs to be explicitly activated or maybe I just didn't wait long enough to see it happen.
As for why it needs 350mb of RAM, let's take a quick look:
$ jcmd main GC.heap_info
Tue Mar 31 10:53:42 2020
61074:
garbage-first heap total 102400K, used 28139K [0x0000000700000000, 0x0000000800000000)
region size 1024K, 0 young (0K), 0 survivors (0K)
Metaspace used 58206K, capacity 65566K, committed 65868K, reserved 1101824K
class space used 10342K, capacity 12810K, committed 12928K, reserved 1048576K
So as a first approximation, even after a GC and subsequent heap shrink, it's holding onto about 3x what it really needs. Total heap memory is only 28mb. That's a bit odd. Perhaps G1 is still not aggressive enough in releasing memory back to the OS. For desktop apps it'd really be better to aggressively let go of heap as fast as possible (NB: native apps don't always do this either).
The "metaspace" is holding a lot of stuff, mostly VM internal data structures. But I thought metaspace was primarily class metadata, but here there's only 10mb of that, so what's filling it up?
There's "jcmd main VM.metaspace" which tells us about 8mb of it is wasted due to fragmentation (I think). It doesn't tell us much about what's going on though.
These numbers look extremely high for a normal Java app. If I recall correctly Clojure [ab]uses the JVM in odd ways, for instance, by creating a class for every single function in the program instead of mapping them to methods as you would expect. This can easily cause a lot of overhead in class metadata compared to a normal program.
However the bulk of the memory usage you see is really just an "optimisation" - it's there, so why not be lazy and leave garbage lying around until you need the space.
The problem is Java sees lots of memory lying unused and figures ... why should I work harder to free it up? It'll waste CPU time and energy and I might need the memory later.
In theory it actually will do background GCs and free memory back to the OS anyway so I'm not sure why it's not happening here; possibly that feature needs to be explicitly activated or maybe I just didn't wait long enough to see it happen.
As for why it needs 350mb of RAM, let's take a quick look:
So as a first approximation, even after a GC and subsequent heap shrink, it's holding onto about 3x what it really needs. Total heap memory is only 28mb. That's a bit odd. Perhaps G1 is still not aggressive enough in releasing memory back to the OS. For desktop apps it'd really be better to aggressively let go of heap as fast as possible (NB: native apps don't always do this either).The "metaspace" is holding a lot of stuff, mostly VM internal data structures. But I thought metaspace was primarily class metadata, but here there's only 10mb of that, so what's filling it up?
There's "jcmd main VM.metaspace" which tells us about 8mb of it is wasted due to fragmentation (I think). It doesn't tell us much about what's going on though.
These numbers look extremely high for a normal Java app. If I recall correctly Clojure [ab]uses the JVM in odd ways, for instance, by creating a class for every single function in the program instead of mapping them to methods as you would expect. This can easily cause a lot of overhead in class metadata compared to a normal program.
However the bulk of the memory usage you see is really just an "optimisation" - it's there, so why not be lazy and leave garbage lying around until you need the space.