Why is finalize wrong?

Peter Levart peter.levart at gmail.com
Wed Sep 3 12:41:01 UTC 2014


Hi Stanimir, Andrew,

On 09/03/2014 01:48 PM, Stanimir Simeonoff wrote:
> Hi Andrew,
>
>
> On Wed, Sep 3, 2014 at 12:58 PM, Andrew Haley <aph at redhat.com> wrote:
>
>> On 03/09/14 01:15, Stanimir Simeonoff wrote:
>>
>>> Like David Lloyd mentioned finalize() can be invoked concurrently to
>>> some methods (if there is no reachability to 'this'). OTOH I see
>>> finalize useful for managing native resources mostly and possibly
>>> stopping (signaling) threads left unmanaged. In that particular case
>>> synchronization is a must, so it comes naturally.
>> But finalize isn't useful for managing native resources because
>> finalizers may or may not run, as you note.  People will test their
>> programs with one implementation then discover, when their programs
>> are deployed, that they sometimes mysteriously fail.  In particular,
>> you might run out of file handles.

Finalizers will eventually run, but they may not run on time. This could 
be fixed though (see below).

>>
>> I meant cleaning up rather than managing per se. To make it clear:
> finalization is no substitute for proper lifecycle - anything that has
> open() should have a following up close(), otherwise it's a bug.
> Finalization still helps as safenet vs leaving native resources totally
> unallocated and leaking. Yet, finalizers have to run somehow since the
> direct (and mapped) buffers very heavily rely on them.

DirectByteBuffers rely on Cleaner(s), which are PhanthomReferences and 
are managed (invoked) by the ReferenceHandler thread directly. 
Finalizers are just enqueued by ReferenceHandler thread, but they are 
invoked by the special FinalizerThread. That's because Cleaner(s) are 
internal JDK mechanism, while finalizers invoke client code so they can 
stall the FinalizerThread. A bug in a client code can bring 
FinalizerThread to a halt.

But there might be a cure for that at least for internal usage (for 
example: file handles)...

>   The direct/mapped
> buffers are ones that don't have proper lifecycle, esp. the mapped ones
> The latter don't offer munmap.
> I believe the java.nio.Bits still includes a notorious call in a
>
> static void reserveMemory(long size, int cap) {
> //check if above the direct memory threashold and...
>          System.gc();
>          try {
>              Thread.sleep(100);
>          } catch (InterruptedException x) {
>              // Restore interrupt status
>              Thread.currentThread().interrupt();
>          }
> }
> in an attempt to cope with the lack of proper and timely finalization.
> Prior the fix the OOM when using DirectBuffers was quite guaranteed unless
> pooling the buffers on your own.
> On a flip note, I think it's kind of a bug as it has to manually call run
> System.runFinalization() prior throwing the OOM, but it opts to sleep 100ms
> instead.

As said, DirectByteBuffer(s) use Cleaner(s), which are more predictable. 
But even with Cleaner(s), a multithreaded test could be constructed 
which reserves direct memory with greater rate than single 
ReferenceHandler thread can unreserve it. Until this was fixed:

https://bugs.openjdk.java.net/browse/JDK-6857566


An internal mechanism similar to that used in this patch could be 
devised for finalizers too. Allocation of native resources (like file 
handles) that are cleaned up by finalizers could use it to retry 
allocation if it fails, helping with cleanup on the way...

Peter

>
> If the finalizers don't run prior the process termination, that's ok as the
> OS should be able to clean up any native resources allocated on its own.
> However finalizers not running is not that different from a weak/phantom
> reference not being enqueued, practically the odds are quite the same. The
> impl. of the finalization is actually a high priority dedicated thread that
> processes a ReferenceQueue.
>
>
>
>> As Jarolsav put it, "I don't think this is explainable to regular Java
>> developers."  As David Lloyd put it, "If you still think that finalize
>> is a good idea, given that it's basically defective *and* there is
>> almost always a better solution, then I will be quite astounded."  As
>> I put it, "finalize is broken."
>>
>>
> I'd not say "broken", as it works exactly as specified. Still I utterly
> agree it requires pretty deep knowledge on the inner works to make it
> somewhat useful.
>
> Stanimir




More information about the core-libs-dev mailing list