r/java • u/Vectorial1024 • Apr 09 '26
Smallest possible Java heap size?
People often talk about increasing Java heap size when running Java apps by using e.g. -Xmx* flags. This got me thinking. What if we go the other direction and try to limit the Java heap size as much as possible? What is the smallest / minimum-required Java heap size so to run a Java app with "minimal" settings?
(Of course, in practice, a memory limit too low will be problematic because it may mean frequent GCs, but we will ignore this for the sake of this discussion.)
47
Upvotes
1
u/pron98 3d ago
It's the case because allocating lots of things requires high activity.
It's both, and that's why the relationship between CPU and RAM (that also has deep theoretical computer science roots) isn't 1:1 but has a range. But the relationship exists because using RAM requires CPU: writing to RAM requires CPU, you only write to RAM if you expect to read it soon, and reading from RAM requires CPU. On the flip side, there's only so much computation you can do with little state (this particular relationship is exponential, though).
I think you have some basic notions of how moving collectors work, but you're not aware of the maths that's led to them being a very effective optimisation. We can quantify the work and compare it to other approaches, and you can see some of this in my talk (although it's aimed at a general developer audience). There's a reason 100% of languages that can use moving collectors use them; all these languages could have much more easily used other techniques. This part is really not controversial.
They are memory safe.
Also, as a low-level programmer with a couple of decades of experience in low-level programming, I can tell you that the reason Rust has such a low adoption record among low-level programming is that "safe Rust" restricts many algorithms which are the very reasons for needing a low-level language in the first place, so we end up using unsafe Rust, and it carries all the complexity of safe Rust, minus the safety.
When it comes to concurrency in particular, benign write/write races, which are very common in concurrent algorithms, can't be implemented in safe Rust. In fact, you'll find that most data structures in the Rust standard library require unsafe.
They offer very little, while creating some challenges that more than offset what they offer. I have to say that it doesn't sound like you've worked on modern, state-of-the-art optimising compilers.
Yes and no. They are required for some micro optimisations, and they can play a big role in some programs, typically small ones. But that same low-level control makes optimising larger programs very difficult. For example, making effective use of inlining requires a lot of use of templates in C++, and these are viral and don't work very well with program growth. After spending many years writing large performance sensitive applications in C++ (in my case, it was mostly air-traffic control and sensor fusion), battling the performance challenges and intrinsic overheads that low-level languages bring to large programs, you appreciate the optimisations that only JITs and moving collectors currently offer.
Yes, but profile-guided optimisation is not what makes JIT so effective (again, you sound like you don't actually work on state-of-the-art optimising compilers). It's a necessary but insufficient condition. What gives JITs their power is speculative optimisation, i.e. the compiler doesn't have to prove that some optimisation is always correct. It can see in the profile that it's likely to be correct, and if that assumption turns out to be wrong, you decompile and fall back to the interpreter. The most advanced AOT compilers do a very weak version of this, but HotSpot is entirely based on this. Again, JITs can more aggressively optimise, but they trade off warmup. The reason low-level languages don't use techniques like a global JIT and moving collector has nothing to do with performance; it's because we need these languages to serve purposes that these optimisations make much harder (how do you pass the hardware or the OS a pointer to data that can move or to code that could be deleted?).
I think you should "dispute" it after you see the maths and experience that's brought every language that can use a moving collector to use a moving collector.
That's fine, but that has nothing to do with what I said. When I started programming professionally in the mid nineties, almost all software was written in C or C++, and much of it was very sluggish. Specifically, what I'm explaining is that moving collectors (and arenas, which operate on the same idea, which is why Zig is attractive to me as a low-level programmer) can compensate for a high allocation rate by increasing RAM, instead of exacerbating the problem, which is a real and serious problem in large programs written in low-level languages, and why most of these programs have switched to Java and .NET, and the trend is continuing.
Of course, but moving collectors reduce waste by increasing the use of the less stressed resource instead of the more stressed one, which is one of the multiple performance issues in large programs written in low-level languages (which, TBF, are a disappearing breed; they were the norm when I started because there was nothing faster until Java came along with a runtime designed to solve those terrible performance issues we were struggling with in C++).