A destructor is a special method that runs automatically when an object is about to be destroyed — typically when it's no longer referenced anywhere and is about to be removed from memory (garbage collected), or in languages with manual memory management, when it's explicitly deleted.
class FileHandler {
private handle
function __construct(path) {
this.handle = openFile(path)
}
function __destruct() {
closeFile(this.handle) // ensures the file handle is always released
}
}
Common uses for destructors:
- Closing file handles, database connections, or network sockets
- Releasing manually-managed memory (critical in C++, where there's no automatic garbage collector)
- Logging or auditing when an object's lifecycle ends
An important nuance across languages: in garbage-collected languages (Java, C#, PHP, Python, JavaScript), you generally cannot rely on exactly when a destructor runs — the garbage collector decides when to reclaim memory, which might be much later than you expect, or, in some edge cases, might not happen predictably at all before the program exits. For this reason, modern code increasingly prefers explicit cleanup patterns instead of relying purely on destructors:
try/finallyblocks — guarantee cleanup code runs regardless of exceptionsusingstatements (C#) /withstatements (Python) / try-with-resources (Java) — deterministic, scope-based resource cleanup- Explicit
close()/dispose()methods — the caller is responsible for calling them when done
C++ is a notable exception: its destructors run deterministically and immediately when an object goes out of scope, which is why the 'RAII' (Resource Acquisition Is Initialization) pattern — tying resource lifetime directly to object lifetime via constructors and destructors — is a cornerstone of idiomatic C++.