Freeing heap objects in C++ is an expensive operation. You may want to look into how that is implemented in the common allocators.
In Java, an allocation is literally a single instruction (adding a value to the heap pointer), and freeing is zero cost (since you just drop the pointer after you're done with it).
By contrast, in C++ both allocation and freeing of heap memory are costly operations (which is there is so much emphasis on stack allocation).
As it turns out, the time taken by the garbage collector in Java (which doesn't exist in C++) is still going to be less than what is spent on managing the same number of heap objects in C++.
The only reason why C++ can be faster than the Java memory management is thanks to all the stack allocation.
The biggest problem Java had was that 90’s era thinking on GC algorithms (most GC languages in the 90’s were using 80’s era GC algorithms) ran out of steam sometime around 2005, when memory availability created situations where GC pause times were impossible to hide. Cliff Click created a JVM that actually ran as a VM, in order to create a cheaper mark phase via page faulting to implement mark on write. Now we have other options.
[0] https://www.usenix.net/legacy/events/vee05/full_papers/p46-c...
[2] https://plover.com/~mjd/misc/hbaker-archive/RealTimeGC.html
There are other hidden costs in Java GC allocation. While most of the time, an allocation will be just two instructions (it cannot just increment a heap pointer, it also has to check whether there's enough free space and jump to a slow path if there isn't, which needs a second instruction), there is also some overhead in the GC tracking of modified references (card marking), either a short pause in each thread while the GC reads its registers (to get any pointers stored there) or extra overhead of having to store references in the stack instead of just in registers, and probably some missed optimization opportunities due to having to do things in a order the GC expects.
The garbage collector itself has some annoying overheads: it can waste cache and memory bandwidth by scanning cold objects, it can cause unnecessary swapping in of swapped out pages, and most importantly, it uses more memory. In fact, that's the main trick of GC: it trades off allocation/deallocation speed and memory usage.
> The only reason why C++ can be faster than the Java memory management is thanks to all the stack allocation.
No, the main reason C++ can be faster than the Java memory management is that it has value objects, which can avoid a lot of heap allocation (and pointer chasing) even without replacing it with stack allocation. Consider for instance an array of objects, in C++ it can be a single allocation, while Java requires one allocation for each object, plus another allocation for the array itself. Which can also be considered a hidden cost of Java GC allocation: to be able to track objects correctly, the GC requires that references to an object point to the beginning of its allocation, so an object cannot be inlined within another and still be tracked correctly by the Java GC.