Search code examples
javagarbage-collectionweak-referencesfinalizer

cleaning up weak reference caches with finalizers?


Assume I have a cache that consists of weak or soft references.

Those weak/soft references need to be closed at some point.

Ideally, the objects should be closed as soon as the objects are removed from the cache by the GC.

Is it appropriate to use a finalizer/cleaner to close those resources while still looping over the cache at the end of the programand closing them manually?

public void CachedObject implements AutoClosable{
    private boolean open;//getter
    public CachedObject{
        //Create resource
        open=true;
    }
    @Override
    public void finalize(){
        super.finalize();
        if(open){
            try{
                close();
             }catch(IllegalStateException e){
                 //Log
            }
        }
    }
    @Override
    public void close(){
        if(open){
            //Close
            open=false;
        }else{
            throw new IllegalStateException("already closed");
        }
    }
}
private WeakHashMap<CachedObject,Object> cache=new WeakHashMap<>();

public void close(){
    //Executed when cache is not needed anymore, e.g. program termination
    for(CachedObject cachedElement:cache){
        if(cachedElement.isOpen()){
             cachedElement.close();
        }
    }
}

Solution

  • Garbage disposal (and therefore finalization) is non-deterministic and quite capricious, so I would recommend that you do not base any important function of your software on it.

    There are no guarantees as to when your objects will be finalized (or cleaned) nor as to even whether they will be finalized. Just try to completely avoid it for any purpose other than perhaps diagnostics, i.e. to generate a warning-level log message telling you that an object is being finalized without first having been closed. But you better close everything explicitly.

    The idea of cached entities being evicted from the cache when the machine needs more memory sounds beautiful at first, but in reality you will discover that:

    • if your garbage collector works aggressively, (default behavior on 64-bit JVMs) your entities will be evicted far more frequently than you had hoped, (way before you start running out of memory,) while

    • if your garbage collector works lazily, (depending on JVM startup options,) your app might run to completion without exhausting its available memory and then the JVM may terminate without finalizing or cleaning anything.