Hacker News new | past | comments | ask | show | jobs | submit login

In the ~15 years I spent building software in C++ I don't recall a single time that I wished for garbage collection. By using RAII techniques, it was always possible (nay, easy!) to write code that cleaned up after itself automatically. I always found it easier to reason about and debug programs because I knew when something was supposed to be freed, and in which thread. The only time that use of simple reference counted smart pointers did not work was when you had cyclic references: but cyclic object graphs caused other problems in any case - so I would always refactor my design to avoid creating them in the first place.

In contrast, in the ~10 years I spent working in Java, I frequently ran into problems with programs which needed an excessive amount of memory to run. I spent countless frustrating hours debugging and "tuning" the JVM to try to reduce the excessive memory footprint (never entirely successfully). And of course, as soon as your JVM needed to interact with native code (e.g. a quant library written in C++), you were even worse off, because there was no language support whatsoever for ensuring when, or even if, some native memory would be freed. It made it even harder to debug these leaks - because the "leak" was always very complicated to reproduce.

Garbage collection is an oversold hack - I concede there are probably some situations where it is useful - but it has never lived up to the claims people made about it, especially with respect to it supposedly increasing developer productivity.






While RAII is a very good technique, it is not the solution to all issues here. I read that some concurrent datastructure implementations would not be possible without a GC. Furthermore, when a project reaches a certain level of complexity, it ends up implementing GC anyway (see Unreal engine).

I agree that RAII does not solve everything - in particular, issues with memory fragmentation. However, I prefer the direction being taken by Rust and suggested by the top level poster: rather than having to run a separate thread which tries to infer which resources are no longer needed, with potentially large runtime costs, instead augment the language semantics with a clear ownership model, and thereby give the compiler enough information to generate code that runs in a stable, predictable memory footprint.

That reminds me of the VILW argument: we will make languages really expressive and compilers really smart, then we can get rid of fancy CPU hardware that do instruction scheduling dynamically.

It doesn’t always work. A dynamic tracing GC (or reference counting or using arenas) can be more efficient in ways that static approaches probably won’t be able to effectively emulate. Rust is a great experiment for seeing how far we can go in this area, so we will see.


RAII has nothing to do with memory fragmentation.

Solving memory fragmentation requires moving objects around. Usually you get that via a moving (or compacting) garbage collector. But there's no reason RAII style resource allocation couldn't move things around.

And there are plenty of garbage collection strategies that don't move objects around.


I was simply stating that RAII does not solve the memory fragmentation issues. Many GC's do solve the memory fragmentation issue.

C++ code is full of use after free problems. You can avoid those with garbage collection.

If you have a use-after-free it means that you made a wrong assumption in your code about the lifetime of the object. A GC would hide the issue and potentially lessen the gravity of the bug but it doesn't resolve the core issue which is that you probably have a fundamental logical problem in your code.

I'm a bit "GC hater" so obviously I'm heavily biased but to me it just means that GC make it easier to write sloppy, poorly architectured code where you have a mess of a data-dependency graph and nobody knows who owns what and when things need to be deleted, so you let the GC (hopefully) figure it out. In my experience this leads to all sorts of issues independently of memory management, such as having obsolete data lingering in certain parts of the program because it's not expunged properly.

IMO GC or not having clear ownership semantics are necessary to properly architecture anything more complicated than a script. And if you have ownership semantics then why do you need a GC anyway?


The nicest point about GC is that, for the vast majority of data, you no longer need to have a concept of ownership at all.

Ownership is not a fundamental architectural property - it is "only" a powerful technique for tracking data with a specific lifetime (of course, even in GC languages you still need proper ownership semantics for some pieces of data).


Ownership itself might not be, but lifetimes are and the two concepts are tightly related.

That's why Java and C# both had to introduce a way of (semi-)automatically closing resources, waiting for the GC was a catastrophe.


Common Lisp, CLU, Mesa/Cedar, Modula-3 and many other system languages with GC always supported those kind of mechanisms.

Java and C# did nothing new there, only catching up with was already the state of the art before they were born.


That’s what I find ironic about C#. In some ways I worry more about and do more manual memory management than I did in C++.

> If you have a use-after-free it means that you made a wrong assumption in your code about the lifetime of the object. A GC would hide the issue and potentially lessen the gravity of the bug but it doesn't resolve the core issue which is that you probably have a fundamental logical problem in your code.

The basic assumption with a GC'ed language is that the lifetime ends once the object isn't referenced anymore. You rarely have to deal with it at all, so those "logic errors" happen much less frequently.

If we look at the major real-world languages without GC (C/C++), it is your job to figure out the lifetime. You will fuck this up unless you structure your program in very disciplined way and that's frankly too much to ask of the vast majority of programmers.


What if your domain problem (say, compilers, symbolic algorithms) doesn't have a clear notion of ownership because everything is a shared DAG that may survive for arbitrarily long? Not every problem has clear ownership imho (and tools in C or C++ in these domains resort to their own refcount/custom GC implementation anyway).

imo you solve these problems by segregating the ownership logic and relationship logic of the data structure.

In other words the DAG would never be traversed to allocate or free memory, each vertex would be held by a vector or other container and the edges represented by an adjacency list/matrix.

It's faster and safer than pointer chasing.


> A GC would hide the issue and potentially lessen the gravity of the bug but it doesn't resolve the core issue which is that you probably have a fundamental logical problem in your code.

No, it solves the problem because resource lifetime is no longer a concern. A field advances as we decrease the number of irrelevant details we need to attend to.

Turns out, most of the time, memory is one such concern. You can argue that we need better approaches when memory is a concern, but that doesn't discount the value of GC as the right default.


Yes, but you can also avoid that and the cost of garbage collection by having an explicit ownership model akin to Rust.

> You can avoid those with garbage collection.

Until you need to close a socket or file or unlock a mutex or respond to an async response handler.

$(cat /opt/propaganda/rust/raii-in-rust.txt)


> C++ code is full of use after free problems.

Once upon a time, this statement may have been true, but it isn't any more. ASAN and Valgrind are widely available, and runtime test suites are much more prevalent than they used to be.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: