Garbage Collection is Harmful

By | 2013-07-05

I don’t like garbage collection.

Fundamentally, my problem with languages that have a garbage collected memory model is this: memory is not the only resource.

Resource Acquisiation is Initialisation. RAII as it’s often abbreviated to. RAII and object oriented languages go together beautifully because the language does the heavy lifting with its automatic calling of constructors and, more particularly, destructors.

Garbage collection encourages you to write non-object-oriented code in your fancy object oriented language, and worse, it doesn’t remove the need for you to manage resources anyway. Garbage collection only works on memory. When the resource isn’t memory, like, mutex’s, texture buffers, file handles, network sockets, you still have to manage the resource yourself, and you also end up needing a pseudo-destructor.

class FileObject {
    public FileObject( name ) {
        open(name);
    }

    // There is no destructor in Java, finalize() is closest
    @Override
    protected finalize() throws Throwable {
        try {
            close();
        } finally {
            super.finalize();
        }
    }

    public void close();
    public void open();
    // ... read, write, the rest ...
};

void someFunction() {
    // Let's say uniquefile.txt can't be opened more than once at any
    // one time
    FileObject openOnce = new FileObject("uniquefile.txt");
    // use
    openOnce.read();

    // ... tidy up ...
}

So, what do we do for tidying up? “Garbage collection” you might cry, “someFunction() will finish, openOnce will go out of scope, making its reference count go to zero, the garbage collector will call the destructor, which will call close()”. You wish that’s what happens. It’s not though.

The problem is this: the garbage collector doesn’t run in-thread, when openOnce has its reference count reduced to zero, that’s all that happens. Or rather, all that is guaranteed to happen. In fact, it’s identical to simply calling openOnce = null;. This is about as close as Java has to a delete; but the destructor isn’t called instantly.

The garbage collector thread will eventually get around to destructing the object, and the file will be closed. In the meantime, if someone else opens “uniquefile.txt”, we will have broken our “only one handle at a time” rule for this resource. We’re forced then to use our pseudo-constructor, in this case close().

void someFunction() {
    // Let's say uniquefile.txt can't be opened more than once at any
    // one time
    FileObject openOnce = new FileObject("uniquefile.txt");
    // use
    openOnce.read();

    openOnce.close();
}

For me then, the question is, why bother with garbage collection?. In all but the simplest of resources (memory), we’re going to have to call a destructor anyway. (Python has exactly the same problems because the destructor is a bit hazy, and has been forced to introduce more complexity with its ‘with’ keyword for creating “contexts”).

This ties in quite closely with my earlier article, where I covered the principle that a properly written resource-managing object with constructor and destructor will, effectively, automatically garbage collect. There’s little evidence to suggest that garbage collection is, these days, any kind of massive resource drain. Perversely though, it means you have to care more about the lifetime of your non-memory resources than you would in a non-GC language.

It’s particularly obvious when you see the hoops Python is slowly adding that get jumped through to deal with all the problems that GC creates. The following “special” members of objects exist all to avoid the supposed complexity of construction/destruction.

  • __new__()
  • __init__()
  • __enter__()
  • __del__()
  • __exit__()

But C++ is the complicated one 😉

Leave a Reply