programming-history

Garbage Collection

APL

Dyalog APL uses a traditional stop-the-world compacting collection when memory runs out. It also uses this opportunity to pack vectors or matrices into more compact representations.

Go

Finalization is supported (via runtime.SetFinalizer).

Finalizers are invoked in dependency order. Accordingly, if a reference cycle contains finalizers, the objects in the cycle are not guaranteed to be finalized nor collected.

Resurrection seems to be supported as finalization can be re-registered during finalization.

Java

Finalization is performed on non-user threads. Finalization order is not defined. Unhandled exceptions in finalizers are ignored.

Object resurrection is supported. Finalizers are only ever invoked once, even if an object is resurrected.

.NET

.NET supports two GC modes: workstation (optimized for responsiveness) and server (optimized for throughput).

Finalization is performed on (a) dedicated thread(s). Finalization order is not defined.

Since .NET 2.0 (2005), unhandled exceptions in finalizers terminate the process.

Objects may be resurrected during finalization by creating a reference to them somewhere that is considered ‘alive’ (such as a static field). Resurrected objects may re-register for finalization by calling GC.ReRegisterForFinalize.

Explicit finalizers should not be used in modern code. Classes that manage external resources should inherit from SafeHandle.

PHP

PHP ≥ 5.3 (2009) uses a hybrid reference-counted/GC approach. Most objects are destroyed due to going out of scope, and have their destructors (methods named __destruct) invoked at that point. Destructors are called in dependency order unless the program is shutting down, in which case the order is undefined.

Objects that are caught in a reference cycle are periodically collected by GC. It is not specified what happens to destructors in this case.

Python

The main implemention (known as ‘CPython’) uses a hybrid reference-counted/GC approach. All objects are refcounted and most are destroyed as soon as they go out of scope. This also means that finalizers (methods called __del__) are run on the thread on which the object became unreferenced (this can cause unexpected deadlocks in unwary code).

Objects that are caught in a reference cycle are periodically collected by GC. In CPython 2, if a cycle contains an object with a destructor, none of the objects are finalized or freed, and they are instead stored in a list (gc.garbage). In CPython ≥ 3.4 (2014), finalizers on all objects in a cycle are invoked in an arbitrary order. In addition, in CPython ≥ 3.4, finalizers are only ever invoked once, even if an object is resurrected.

Other implementations such as PyPy implement full GC as in other languages, and so finalization occurs on non-user threads. In order to maintain parity with CPython finalization semantics, PyPy performs finalization in a ‘topological’ manner.

Ruby

The main Ruby implementation (also known as ‘CRuby’ or ‘MRI’) uses a generational garbage collector as of version 2.1 (2013). Earlier versions used mark & sweep.

Finalization is supported (via ObjectSpace.define_finalizer). For some reason, capturing a reference to self in the a finalizer proc allegedly prevents the object from being collected.