I have a system that keeps running out of disk space because that garbage collector doesn't free the objects that hold the file handles fast enough. (Files are created and deleted all the time, but since the process still has an open file handle, the OS keeps it on the disk).
The objects are shared, so a simple try { ... } finally { close(); }
will not do.
It seems to me that my best option is to implement reference counting on the objects, and close the file handles when the reference count goes to 0. However, I'm reluctant to implement it all by myself, as I suspect there are subtle issues with regards to concurrency.
Sadly, googling for "reference counting in java" doesn't bring any useful results. So my question is: are there any resources (articles, sample code, libraries) that can help implement reference counting?
Do not depend on the garbage collector. It is intentionally designed not to be dependable.
If "shared" means that you use it several places in your code so you cannot just close it, I would suggest you change your code to have a central pool of files, where you can "check out" a file handle to be used in your code locally. The close() procedure then returns the file handle to the pool. Keep track of your handles, when all handles for a given file is returned to the pool, you close the file for good.
Sadly, googling for "reference counting in java" doesn't bring any useful results.
Sadly, this continues to be true 8 years later.
But, no longer! I've pulled out Netty's reference counting bits, polished them off quite a bit, and made them into a separate library, almson-refcount.
The basic reference counting functionality is simple and straightforward. There is a single base class, ReferenceCountedObject
. It has a single overrideable method, destroy
. It provides retain
and release
which manage an internal reference counter using a thread-safe and efficient AtomicFieldUpdater. release
will call destroy
on the same thread, and because of memory ordering semantics between different calls to release
, you shouldn't need to worry about the thread-safety of your destroy
even in a multi-threaded application. The class implements AutoCloseable
and provides a method close
which simply calls release
. This allows it to be used in try-with-resources.
There is no finalization mechanism which tries to call destroy
in case you forget to call release! Finalization presents big challenges, including concurrency issues and even premature finalization, especially in the general case. (If you insist on having finalizers, you can still use them or the higher-performance java.lang.ref.Cleaner
.)
Instead, there is a clever leak detection system. It uses a similar mechanism to finalization. Because its only responsibility is detecting leaks and recording debugging info, there is nothing you need to do to make it work correctly (except turn it on).
The main things changed vs Netty are:
You can do reference counting by wrapping your object inside a WeakReference
and then using the ReferenceQueue
. However, it seems like you just want to discover when your handle is no longer referenced, so you don't really need to count at all.
But the same method (ie a WeakReference
) will do; just close a handle when the referent becomes null
. However, you may need a bespoke subclass of WeakReference
with an extra reference to the file handle, so that you can close it (otherwise you won't have access to the file handle). For example:
public class WeakFileReference extends WeakReference<File> {
private final File handle;
public WeakFileReference(File handle, ReferenceQueue q) {
super(handle, q);
this.handle = (File)handle.clone();
}
}
I've not checked this completely and can't be sure how you are using the File
object in your program: I assume that you are sharing a File
instance around.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With