Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference counting in Java

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?

like image 753
itsadok Avatar asked Oct 17 '10 11:10

itsadok


3 Answers

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.

like image 147
Thorbjørn Ravn Andersen Avatar answered Oct 11 '22 07:10

Thorbjørn Ravn Andersen


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:

  • An actual, usable base class for all of your reference-counted objects.
  • A more elegant interface with fewer methods.
  • Simpler, cleaner code with less cruft.
  • Improvements to the documentation, presets, and output of the leak detector.
like image 25
Aleksandr Dubinsky Avatar answered Oct 11 '22 06:10

Aleksandr Dubinsky


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.

like image 33
oxbow_lakes Avatar answered Oct 11 '22 07:10

oxbow_lakes