On Wednesday, 13 January 2021 at 18:58:56 UTC, Marcone wrote:
I've always heard programmers complain about Garbage Collector GC. But I never understood why they complain. What's bad about GC?

SHORT VERSION: While garbage collection is great for many applications, GC also has some significant disadvantages. As a systems programming language, D attracts users who care about the disadvantages of GC, but D's design prevents it from mitigating the downsides of GC to the extent that less powerful languages like Java can.

LONG VERSION: There are many different garbage collector designs, so not all of these criticisms apply to all garbage collector designs. Nevertheless, most every design suffers from at least some of these problems:

1) Freezes/pauses: many garbage collectors have to pause all other threads in the program for some significant time in order to collect. This takes somewhere between a few ms and a few s, depending on how much data needs to be scanned.

For interactive applications, these pauses can cripple the program's performance. 60 FPS, or 16ms per frame, is a typical target rate for modern user interfaces, video playback, and games. When any given frame may be subject to a 5ms pause by the GC, the processing for *every* frame must be limited to what can be accomplished in 11ms, effectively wasting 30% of the available CPU performance of *all cores* on most frames, when no collection was necessary. But, 5ms is on the low end for GC pauses. Pauses longer than 16ms are common with some GC designs, guaranteeing dropped frames which are distracting and unpleasant to the user.

For real-time applications such as hardware control systems, a pause that is too long could actually break something or injure someone.

2) Much higher memory consumption: all practical heap memory management schemes have some overhead - additional memory that is consumed by the manager itself, rather than the rest of the program's allocations. But, garbage collectors typically require three to ten times the size of the data for good performance, as opposed to two times or less for reference counting or manual management.

3) RAII doesn't work properly because the GC usually doesn't guarantee that destructors will be run for objects that it frees. I don't know why this is such a common limitation, but my guess is that it is due to one or both of: a) Collection often happens on a different thread from an object's allocation and construction. So, either all destructors must be thread-safe, or they just can't be run. b) A collection may occur at some awkward point where the program invariants depended on by non thread-safe destructors are violated, like inside of another destructor.

It is certainly possible to retrofit correct resource management on top of a GC scheme, but the logic required to determine when to close file handles (for example) is often the same as the logic required to determine when memory can safely be freed, so why not just combine the two and skip the GC?

GC has significant advantages, of course: simplicity (outside the GC itself), safety, and, for better designs, high throughput. But, the D users are more critical of GC than most for good reason:

I) D is a systems programming language. While a good GC is the best choice for many, many applications, the kinds of applications for which GC is inappropriate usually require, or at least greatly benefit from, the full power of a systems programming language. So, the minority of programmers who have good reason to avoid GC tend to leave other languages like Java, C#, python, and JavaScript and come to us (or C, C++, Zig, or Rust).

II) Historically, D's GC was embarrassingly bad compared to the state-of-the-art designs used by the JVM and .NET platforms. D's GC has improved quite a lot over the years, but it is not expected to ever catch up to the really good ones, because it is limited by other design decisions in the D language that prioritize efficient, easy-to-understand interoperability with C-style code over having the best possible GC.

In particular, D's GC is a stop-the-world design that must pause *all* threads that may own any GC memory whenever it collects, and thus it fully suffers from problem (1) which I described earlier. Also, it used to have the additional problem that it leaked memory by design (not a bug). This is mostly fixed now, but for some reason the fix is not enabled by default?! https://dlang.org/spec/garbage.html#precise_gc

(In before someone answers with, "Nothing. It works for me, so people who say it's bad are just stupid and don't profile.")

Reply via email to