Jump to page: 1 29  
Page
Thread overview
Why many programmers don't like GC?
Jan 13
Marcone
Jan 14
claptrap
Jan 15
welkam
Jan 15
welkam
Jan 15
IGotD-
Jan 15
IGotD-
Jan 15
jmh530
Jan 15
welkam
Jan 15
aberba
Jan 15
aberba
6 days ago
aberba
6 days ago
aberba
6 days ago
Arafel
6 days ago
aberba
5 days ago
ddcovery
5 days ago
ddcovery
5 days ago
Imperatorn
6 days ago
H. S. Teoh
Jan 15
welkam
6 days ago
Imperatorn
Jan 13
tsbockman
Jan 13
mw
Jan 13
tsbockman
Jan 14
mw
Jan 14
mw
Jan 15
welkam
Jan 15
jmh530
Jan 15
jmh530
Jan 14
sighoya
Jan 14
sighoya
Jan 14
Basile B.
Jan 14
sighoya
Jan 14
ddcovery
Jan 14
IGotD-
Jan 14
Dukc
Jan 15
welkam
January 13
I've always heard programmers complain about Garbage Collector GC. But I never understood why they complain. What's bad about GC?
January 13
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?

I would guess because of performance issues.
January 13
On Wed, Jan 13, 2021 at 06:58:56PM +0000, Marcone via Digitalmars-d-learn wrote:
> I've always heard programmers complain about Garbage Collector GC. But I never understood why they complain. What's bad about GC?

It's not merely a technical issue, but also a historical and sociological one.  The perception of many people, esp. those with C/C++ background, is heavily colored by the GC shipped with early versions of Java, which was stop-the-world, inefficient, and associated with random GUI freezes and jerky animations.  This initial bad impression continues to persist today esp. among the C/C++ crowd, despite GC technology having made great advances since those early Java days.

Aside from skewed impressions, there's still these potential concerns with the GC:

(1) Stop-the-world GC pauses (no longer a problem with modern
generational collectors, but still applies to D's GC);

(2) Non-deterministic destruction of objects (D's dtors are not even
guaranteed to run if it's a GC'd object) -- you cannot predict when an
object will be collected;

(3) GC generally needs more memory than the equivalent manual memory
management system.

(1) and (2) can be mitigated in D in various ways, e.g., prefer structs over classes to reduce GC load, use GC.stop, GC.collect to control when collections happen, etc.. Use RAII structs or scope guards for things that need deterministic destruction. (Or use malloc/free yourself.) (3) is generally not a big problem unless you're targeting low-memory devices, in which case you already have to do many things manually anyway, so you generally won't be relying on the GC in the first place.

There's also the matter of ROI: it's *much* easier, and faster, to write GC code than code with manual memory management.  For 90% of software, none of the above concerns matter anyway, and you're just wasting your time/energy for essentially no benefit (and lots of disadvantages, like wasting time/energy debugging hard-to-trace pointer bugs and subtle memory corruptions).  GC code also tends to be cleaner: your APIs don't have to be polluted with memory-management paraphrenalia that tend to percolate all over your code and make it hard to read and harder to maintain.  You get to focus your mental resources on making actual progress in your problem domain instead of grappling with memory management issues at every turn.  And most of the time, it's Good Enough(tm); the customer won't even notice a difference.  The price of using a GC is far dwarved by the benefits it brings.

Without a GC you're pouring down blood and sweat just to make a little progress in your problem domain, and the whole time you're plagued with pointer bugs, memory corruptions, and all sorts of lovely issues that come with just one tiny mistake in your code but takes hours, days, or even months to fix. And your code will be convoluted, your API's ugly, fragile, and hard to maintain. The ROI simply makes no sense, except for very narrow niches like hard real-time software (where a patient may die if you get an unexpected GC pause while controlling a radiation treatment device) and game engine cores.


But there is no convincing a hard-core GC hater sometimes. You do so at your own risk. :-D


T

-- 
Life begins when you can spend your spare time programming instead of watching television. -- Cal Keegan
January 13
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.")
January 13
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?

tsbockman gave a good answer.

In short:

- You need to design the language for GC for it to be a satisfying solution for interactive applications. For D and C++ it is bolted on... which is not great.

- You will use roughly twice as much memory with GC (you get more garbage).

- You will get more uneven performance with GC (humans are smarter).

January 13
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?

I want to stress: in D you can *MIX* GC with manual memory management, which gives you the best of both world.

I summarized my experience in one earlier post, (and copy & pasted below); and I also add the code to jdiutil: Just-Do-It util

https://wiki.dlang.org/Memory_Management#Explicit_Class_Instance_Allocation

https://github.com/mingwugmail/jdiutil/blob/master/source/jdiutil/memory.d


===========================================
https://forum.dlang.org/post/hzryuifoixwwywwifwbz@forum.dlang.org

One of the upside of D I like is that one can mix GC with manual memory management:

https://dlang.org/library/core/memory/gc.free.html

which gives you the best of both world.

Currently I have a personal project, initially I was solely relying on GC just like in Java: allocate all the objects via `new`, and let the GC take care of all the bookkeeping. But there is a particular set of objects which takes the majority of memory consumption of the program, and even after I carefully removed all the reference after the object is no longer used, the program still use lots of memory because GC collection is un-predictable, both in terms of timing and efficiency.

Then I decided to do manual core.memory.GC.free just for that particular objects, (it was not very easy in a multi-threaded program to make all the logic right, but eventually I got it done). And the resulting program now only use ~10% of the memory it used to use.

I think this flexibility to mix GC & manual memory management is very unique in D. Actually I'm not sure if it can be done in other languages at all.
===========================================


January 13
On Wednesday, 13 January 2021 at 21:56:58 UTC, mw wrote:
> I think this flexibility to mix GC & manual memory management is very unique in D. Actually I'm not sure if it can be done in other languages at all.

Yes, this is one of the great things about D.

There are miscellaneous problems with the D runtime and the D standard library that make it harder than it needs to be, though. Improvements I would like to see in the future:

1) Finalize std.experimental.allocator

2) Good, safe, flexible reference counting module in the standard library (this requires further development of dip1000 and the like, I think).

3) Upgrade core.thread to fully support @nogc. I shouldn't lose access to Thread.sleep and the like just because a thread isn't being monitored by the GC.

4) Single-threaded versions of various components related to memory management that are more efficient because they don't need to be thread-safe. For example, people say that reference counting is slow because incrementing and decrementing the count is an atomic operation, but most references will never be shared between threads so it is just a waste to use atomics.

Still, all of these issues can be worked around today; D lacks high quality standards in this area more than it lacks necessary features.
January 14
On Wednesday, 13 January 2021 at 21:56:58 UTC, mw wrote:
> I think this flexibility to mix GC & manual memory management is very unique in D. Actually I'm not sure if it can be done in other languages at all.

It sure can. Most AOT languages that provide GC also provide C-interfaces and manual memory management. C++ also had the Boehm-collector since the 90s. Chrome uses Oilpan, a library-style GC with write barriers and incremental collection.






January 14
On Thursday, 14 January 2021 at 00:15:12 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 13 January 2021 at 21:56:58 UTC, mw wrote:
>> I think this flexibility to mix GC & manual memory management is very unique in D. Actually I'm not sure if it can be done in other languages at all.
>
> It sure can. Most AOT languages that provide GC also provide C-interfaces and manual memory management. C++ also had the Boehm-collector since the 90s. Chrome uses Oilpan, a library-style GC with write barriers and incremental collection.

ok, what I really mean is:

... in other "(more popular) languages (than D, and directly supported by the language & std library only)" ...
January 14
On Thursday, 14 January 2021 at 00:37:29 UTC, mw wrote:
> ok, what I really mean is:
>
> ... in other "(more popular) languages (than D, and directly supported by the language & std library only)" ...

Well, even Python supports both, if you want to, so... I suppose you mean system level programming languages? The reality is that GC for a system level programming language is not popular to begin with. In that domain it is fairly common to not use the standard library and use custom runtimes as we can see for C and C++.

Anyway, what makes the D GC weak is exactly that there is not much support for it in the D language or the compilers, only in the runtime and the bare minimum of RTTI. LLVM support more advanced GC features than D provides.

So, the D GC doesn't do much more for programmers than Boehm. And Boehm is not popular either...

Oilpan, that Chrome uses has more advanced features than the D GC, and does what most system level programmers want: limits it to designated GC types and supports incremental collection. The downside is that each Oilpan GC type also has to specify which pointers to trace, but then again, not being able to do that in D is a disadvantage...

For systems programming, I think D would be better off appropriating the approach taken by Oilpan and mix it with reference counting, but make it a language/compiler feature. That is at least a proven approach for one big interactive application. Basically make "D class" objects GC and everything else RC or manual.



« First   ‹ Prev
1 2 3 4 5 6 7 8 9