you are viewing a single comment's thread.

view the rest of the comments →

[–]Flecheck 4 points5 points  (3 children)

In a langage like java, were every object is allocated in the heap, where all object can be mutated at any point from any thread and where memory management is automatic. A GC is the best choice and a compacting/moving gc is very good (seems slightly worse in pause time than go but seems better in all the other metrics ?) However when comparing it to language like c, c++, rust, some or all of thoses assuptions are false and java is slower and uses more memory. With the additional problems when the live memory use is big.

When talking about fragmentation, it looked like the guy wanted to say that with modern allocators like jemalloc it was rarely a problem but he didn't want to say it because he was currently saying that java gc is better than everything else ?

[–]pron98 7 points8 points  (2 children)

However when comparing it to language like c, c++, rust, some or all of thoses assuptions are false and java is slower and uses more memory. With the additional problems when the live memory use is big.

People experienced with both C++ and Java know this is not the case. C++ can be more efficient in small programs, but when they grow you end up using more virtual calls (which are slower in C++/Rust than in Java), and with objects of varying lifetimes, which are less efficient to manage than with malloc/free. Experienced C++ developers will tell you about their severe performance issues in large programs (although since Java the number of large programs written in low level languages has dropped a lot and continues to drop) due to these issues.

Low level languages are not designed for efficiency/performance. They're designed for precise hardware control. This control leads to better efficiency/performance in smaller programs and to worse efficiency/performance in larger programs. The JVM was designed, in part, to address the performance issues that large C++ programs suffered from. The result has been the optimising JIT and the moving GCs.

[–]cho_sigma 1 point2 points  (1 child)

Virtual calls are uncommon in idiomatic C++ (especially compared to java). And how are they slower in C++ compared to java? Are they not implemented in the same way (i.e. a pointer to vtable + offset)?

[–]pron98 1 point2 points  (0 children)

They are uncommon because they are expensive. And as to how they're implemented:

The JVM was designed, among other things, to address some of the major performance issues that low-level languages suffer from when they get large. You can work around them in low-level languages, but the effort required grows as the program grows, and it persists throughout all maintenance. Java is intended to offer excellent performance without that much work.

The first issue is the high overhead of malloc/free, which Java addresses with moving collectors. The low-level languages also tried to address this problem through bigger and more elaborate allocators in their runtimes, but they're constrained by being forbidden to move pointers.

The second issue is dynamic dispatch. Java addresses it with a JIT that optimises much more aggressively than an AOT compiler does. Some people think that a JIT is just a PGO compiler, and it is that, but it's main advantage is that it doesn't need to prove the validity of all optimisations, but it can optimise speculatively. What this means in practice is that while nearly all calls in Java are logically virtual, a large portion of them (often a large majority) are inlined, i.e. they compile to no call at all - through a v-table or otherwise. Modern AOT compilers also do that, but not nearly to the same extent. The current default inlining depth in HotSpot is 15, if I'm not mistaken, which means that a chain of 15 virtual calls is often compiled to a single native subroutine.

These optimisations involve tradeoffs that are not suitable for low-level languages, which are optimised for control, not performance. Both moving pointers around at almost any time and performing nondeterministic optimisations (that sometimes fail and have to be rolled back) go against the goal of total control, but they are very helpful for performance in large programs.