In computer science, a finalizer or finalize method is a special method that performs finalization, generally some form of cleanup. A finalizer is executed during object destruction, prior to the object being deallocated, and is complementary to an initializer, which is executed during object creation, following allocation. Finalizers are strongly discouraged by some, due to difficulty in proper use and the complexity they add, and alternatives are suggested instead, mainly the dispose pattern (see problems with finalizers).
The term finalizer is mostly used with programming languages that use garbage collection, such as object-oriented, archetypically Smalltalk, and functional, archetypically ML. This is contrasted with a destructor, which is a method called for finalization in languages with deterministic object lifetimes, archetypically C++. These are generally exclusive: a language will have either finalizers (if automatically garbage collected) or destructors (if manually memory managed), but in rare cases a language may have both, as in C++/CLI and D, and in case of reference counting (instead of tracing garbage collection), terminology varies. In technical use, finalizer may also be used to refer to destructors, as these also perform finalization, and some subtler distinctions are drawn – see terminology. The term final also indicates a class that cannot be inherited; this is unrelated.
Terminology
The terminology of finalizer and finalization versus destructor and destruction varies between authors and is sometimes unclear.
In common use, a destructor is a method called deterministically on object destruction, and the archetype is C++ destructors; while a finalizer is called non-deterministically by the garbage collector, and the archetype is Java <code>finalize</code> methods.
For languages that implement garbage collection via reference counting, terminology varies, with some languages such as Objective-C and Perl using destructor, and other languages such as Python using finalizer (per spec, Python is garbage collected, but the reference CPython implementation since its version 2.0 uses a mix of reference counting and garbage collection). This reflects that reference counting results in semi-deterministic object lifetime: for objects that are not part of a cycle, objects are destroyed deterministically when the reference count drops to zero, but objects that are part of a cycle are destroyed non-deterministically, as part of a separate form of garbage collection.
In certain narrow technical use, constructor and destructor are language-level terms, meaning methods defined in a class, while initializer and finalizer are implementation-level terms, meaning methods called during object creation or destruction. Thus for example the original specification for the C# language referred to "destructors", even though C# is garbage-collected, but the specification for the Common Language Infrastructure (CLI), and the implementation of its runtime environment as the Common Language Runtime (CLR), referred to "finalizers". This is reflected in the C# language committee's notes, which read in part: "The C# compiler compiles destructors to ... [probably] instance finalizer[s]". This terminology is confusing, and thus more recent versions of the C# spec refer to the language-level method as "finalizers".
Use
Finalization is mostly used for cleanup, to release memory or other resources: to deallocate memory allocated via manual memory management; to clear references if reference counting is used (decrement reference counts); to release resources, particularly in the resource acquisition is initialization (RAII) idiom; or to unregister an object. The amount of finalization varies significantly between languages, from extensive finalization in C++, which has manual memory management, reference counting, and deterministic object lifetimes; to often no finalization in Java, which has non-deterministic object lifetimes and is often implemented with a tracing garbage collector. It is also possible for there to be little or no explicit (user-specified) finalization, but significant implicit finalization, performed by the compiler, interpreter, or runtime; this is common in case of automatic reference counting, as in the CPython reference implementation of Python, or in Automatic Reference Counting in Apple's implementation of Objective-C, which both automatically break references during finalization. A finalizer can include arbitrary code; a particularly complex use is to automatically return the object to an object pool.
Memory deallocation during finalization is common in languages like C++ where manual memory management is standard, but also occurs in managed languages when memory has been allocated outside of the managed heap (externally to the language); in Java this occurs with Java Native Interface (JNI) and <code>ByteBuffer</code> objects in New I/O (NIO). This latter can cause problems due to the garbage collector being unable to track these external resources, so they will not be collected aggressively enough, and can cause out-of-memory errors due to exhausting unmanaged memory – this can be avoided by treating native memory as a resource and using the dispose pattern, as discussed below.
Finalizers are generally both much less necessary and much less used than destructors. They are much less necessary because garbage collection automates memory management, and much less used because they are not generally executed deterministically – they may not be called in a timely manner, or even at all, and the execution environment cannot be predicted – and thus any cleanup that must be done in a deterministic way must instead be done by some other method, most frequently manually via the dispose pattern. Notably, both Java and Python do not guarantee that finalizers will ever be called, and thus they cannot be relied on for cleanup.
Due to the lack of programmer control over their execution, it is usually recommended to avoid finalizers for any but the most trivial operations. In particular, operations often performed in destructors are not usually appropriate for finalizers. A common anti-pattern is to write finalizers as if they were destructors, which is both unnecessary and ineffectual, due to differences between finalizers and destructors. This is particularly common among C++ programmers, as destructors are heavily used in idiomatic C++, following the resource acquisition is initialization (RAII) idiom.
Syntax
Programming languages that use finalizers include C++/CLI, C#, Clean, Go, Java, JavaScript and Python. Syntax varies significantly by language.
In Java, a finalizer is a method called <code>finalize</code>, which overrides the <code>Object.finalize</code> method.
In JavaScript, FinalizationRegistry allows requesting a callback when an object is garbage-collected.
In Python, a finalizer is a method called <code>__del__</code>.
In Perl, a finalizer is a method called <code>DESTROY</code>.
thumb|UML class in C# containing a constructor and a finalizer.
In C#, a finalizer (called "destructor" in earlier versions of the standard) is a method whose name is the class name with <code>~</code> prefixed, as in <code>~Foo</code> – this is the same syntax as a C++ destructor, and these methods were originally called "destructors", by analogy with C++, despite having different behavior, but were renamed to "finalizers" due to the confusion this caused.
In C++/CLI, which has both destructors and finalizers, a destructor is a method whose name is the class name with <code>~</code> prefixed, as in <code>~Foo</code> (as in C#), and a finalizer is a method whose name is the class name with <code>!</code> prefixed, as in <code>!Foo</code>.
In Go finalizers are applied to a single pointer by calling the <code>runtime.SetFinalizer</code> function in the standard library.
Implementation
A finalizer is called when an object is garbage collected – after an object has become garbage (unreachable), but before its memory is deallocated. Finalization occurs non-deterministically, at the discretion of the garbage collector, and might never occur. This contrasts with destructors, which are called deterministically as soon as an object is no longer in use, and are always called, except in case of uncontrolled program termination. Finalizers are most frequently instance methods, due to needing to do object-specific operations.
The garbage collector must also account for the possibility of object resurrection. Most commonly this is done by first executing finalizers, then checking whether any objects have been resurrected, and if so, aborting their destruction. This additional check is potentially expensive – a simple implementation re-checks all garbage if even a single object has a finalizer – and thus both slows down and complicates garbage collection. For this reason, objects with finalizers may be collected less frequently than objects without finalizers (only on certain cycles), exacerbating problems caused by relying on prompt finalization, such as resource leaks.
If an object is resurrected, there is the further question of whether its finalizer is called again, when it is next destroyed – unlike destructors, finalizers are potentially called multiple times. If finalizers are called for resurrected objects, objects may repeatedly resurrect themselves and be indestructible; this occurs in the CPython implementation of Python prior to Python 3.4, and in CLR languages such as C#. To avoid this, in many languages, including Java, Objective-C (at least in recent Apple implementations), and Python from Python 3.4, objects are finalized at most once, which requires tracking if the object has been finalized yet.
In other cases, notably CLR languages like C#, finalization is tracked separately from the objects themselves, and objects can be repeatedly registered or deregistered for finalization.
Problems
Depending on the implementation, finalizers can cause a significant number of problems, and are thus strongly discouraged by a number of authorities. These problems include:
- Finalizers may cause deadlocks if synchronization mechanisms such as locks are used, due to not being run in a specified order and possibly being run concurrently.
- Finalizers that are run during program termination cannot rely on the usual runtime environment, and thus may fail due to incorrect assumptions – for this reason finalizers are often not run during termination.
Further, finalizers may fail to run due to objects remaining reachable beyond when they are expected to be garbage, either due to programming errors or due to unexpected reachability. For example, when Python catches an exception (or an exception is not caught in interactive mode), it keeps a reference to the stack frame where the exception was raised, which keeps objects referenced from that stack frame alive.
In Java, finalizers in a superclass can also slow down garbage collection in a subclass, as the finalizer can potentially refer to fields in the subclass, and thus the field cannot be garbage collected until the following cycle, once the finalizer has run. The introduction of Java in 1995 contained <code>finalize</code> methods, which popularized the term and associated it with garbage collection, and languages from this point generally make this distinction and use the term "finalization", particularly in the context of garbage collection.
Notes
References
Further reading
External links
- "Finalize Instead Of Proper Destructor", WikiWikiWeb – comparison of Java finalizers with C++ destructors
- Krill, Paul; "Oracle recommends axing Java object finalizer", JavaWorld March 29, 2017.
de:Garbage Collection#Finalisierung
