There are a number of reasons to avoid using Java Finalization (that is, objects that override the
finalize() method). From the GC performance perspective, the main problem with finalization is that when GC determines that a finalizable object
F is unreachable, it doesn’t clean up
F immediately, as it does with other unreachable objects. Instead, the GC places
F on the finalization queue, which is served by a single thread. Only when that thread executes the
finalize() method of
F becomes eligible for cleanup. Thus, managing each finalizable object requires extra time and memory, and when a finalization thread is unable to keep up, it may result in a big backlog of unfinalized objects. In the worst case – when some finalizer blocks – the whole finalization queue will block, causing a memory leak and eventual
OutOfMemoryError. Fortunately, the above situations are easy to detect via heap dump analysis. JXRay finds and marks all objects awaiting finalization, so when they cause a sizable leak, it’s immediately visible.
However, it turns out that in some situations, typically when the finalizable objects are allocated at a high rate and there is already enough other pressure on the GC, presence of even short and quick finalizer code may lead to big GC performance degradation. We once had to debug such an accident, and it was almost by luck that we noticed that in the heap dumps of the problematic app there were between 70K and 130K of unfinalized objects of certain type. These objects were taking little space: less than 1% of the used heap. However, once
finalize() (which was not essential for these objects) was removed, the GC performance got back to 100% immediately.
Here is the conclusion: if your app starts to experience long GC pauses that are hard to explain, it is worth taking a heap dump, analyzing it with JXRay and checking the “Objects awaiting finalization” section. If such objects are found, then even if they take little memory, it’s still worth checking their number and estimating their allocation rate. If it turns out that the finalizable objects are allocated at the rate of about 10K per second or more, there is a good chance that GC performance degradation is caused by them.
Be very careful with finalization!