Why is finalize wrong?

Andrew Haley aph at redhat.com
Wed Sep 3 13:57:57 UTC 2014


Hi,

On 09/03/2014 12: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.
>>
> 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. 

But it doesn't help: as I discovered when trying to debug the PKCS1
crypto provider, long-lived objects move into the old gen, and until
there is a real shortage of heap memory, there is no old gen
collection, so native memory becomes exhausted.  It is impossible to
do it correctly; and we programmers comfort ourselves with the idea
that we're doing our best.

> Yet, finalizers have to run somehow since the direct (and mapped)
> buffers very heavily rely on them. The direct/mapped buffers are
> ones that don't have proper lifecycle, esp. the mapped ones
> The latter don't offer munmap.

True enough, but I suspect we only get away with it because it's not
always necessary to munmap a file: if it's not in use it'll all be
swapped out of memory, and the only significant resource leak will be
a file handle.  On 64-bit systems we still have plenty of address
space for this.  For now, anyway.

I admit that it's quite hard to solve the MappedByteBuffer.close()
problem efficiently while still providing security guarantees.

To some extent this discussion comes down to personal preference: I
don't think that incorrect solutions to the problem of resource
management help, because they give a false sense of security.  And I
think some people are in denial about finalization.

On a more positive note, I've had an idea.  How about we change the
generational collectors so that no object with a finalizer is ever
moved into the old gen?  this seems reasonable to me, given that the
primary purpose of finalizers is to free resources.

Andrew.



More information about the core-libs-dev mailing list