M&S also doesn't necessitate having a moving and compacting GC. That's the thing that actually makes the JVM's heap greedy.

Go also does M&S and yet uses less memory. Why? Because go isn't compacting, it's instead calling malloc and free based on the results of each GC. This means that go has slower allocation and a bigger risk of memory fragmentation, but also it keeps the go memory usage reduced compared to the JVM.