In Java, memory management is carried out through automatic garbage collection (Garbage Collector, GC). The JVM tracks objects in memory by itself: if there are no more references to an object, it becomes unreachable and can be deleted.
GC consists of different phases, such as Mark, Sweep, and sometimes Compact. In the Mark phase, objects that can be reached from the roots (GC roots) are marked as alive. After that, the Sweep phase begins: unused objects are deleted. Sometimes Compact is performed — defragmentation of memory.
There are different types of GC: Serial, Parallel, CMS, G1. They should be chosen depending on the type of load on the application.
List<byte[]> dataList = new ArrayList<>(); while(true) { dataList.add(new byte[1024*1024]); // Objects remain reachable }
In this example, objects will never become unreachable, so the GC will not delete them, and an OutOfMemoryError will occur.
If an object has no more references, when is the call to its finalizer (finalize()) guaranteed?
Answer: The call to the finalize() method is not guaranteed at all! It may be called zero times or with a delay. You cannot rely on finalize() for resource cleanup.
@Override protected void finalize() throws Throwable { // May not be executed! }
Story
In a large e-commerce system, developers hoped to automate the cleanup of temporary files using the
finalize()method, but due to the rare invocation of this method, the cache overflowed with temporary files, leading to disk shortages and downtimes.
Story
In a high-load REST API, an internal cache of references to large objects was accidentally saved, preventing memory from being freed. After the application crashed with an OutOfMemoryError, the analysis showed a bug in the caching method.
Story
Finalizers were used to release network connections, considering it a "reliable way". This led to connections hanging in the system indefinitely, causing locks and depletion of the connection pool.