It's nice to try to create awareness however, but the heap/stack is not the first thing that would come to my mind tbh.
It's nice to try to create awareness however, but the heap/stack is not the first thing that would come to my mind tbh.
High level languages go out of the way to make sure you don't need to worry about this stuff, so it's natural people just don't. And a lot of the time, the language runtime applies to many optimisations that you can't really know what will happen (and even if you know, that can probably change as the language may not guarantee the behaviour will never change).
If you do JS/Python/Ruby and similar languages, there's zero reason to know the difference between heap and stack. In fact, I think most of the time what may look like a heap allocation may be optimised by the JIT to become a simple variable on the stack!
But of course, if you want to up your game and do more low level stuff that can squeeze the most of performance from the hardware, it's important to learn that stuff. But you probably need a language that enables you to do that. Rust, C, D, Nim etc. all allow you to tell the compiler exactly how to manage memory. Which is a blessing or a curse depending on whether you care about it or not.
Languages like Java and C# are right in the middle: they do let you manage some stuff (e.g. ArrayList capacity that you mention) while hiding others (Java doesn't let you directly ask for some reference type to go on the stack but does escape analysis to try really hard to put your Object memory on the stack for you anyway).
† It should because even a very high level programmer can significantly impact performance by providing useful hints here, but it doesn't have to. And hey C++ programmers think they're writing low level code and their hint API is rubbish (and the language's inventor says you shouldn't bother providing hints as a result) so apparently you're in good company.
It's actually interesting where capacity is used, and of those places where it's available to the programmer to influence. In principle a lot of types could benefit from smarter growth strategies, but often only the growable array type (and maybe a string type if your language at least knows those aren't just growable arrays) is given a suitable API.
For example even crappy 1980s-style hash tables could avoid allocation overhead with a pre-allocation scheme based on size hints, ie knowing capacity. All the Open addressed hash tables can do even better here because it's a natural fit for them.
Is that a new hotspot feature? I thought it can only do scalar replacement (avoiding object allocation and keeping the object fields in registers).