FinalizableReferenceQueue to go

160 views
Skip to first unread message

Joe Kearney

unread,
Oct 5, 2011, 7:19:12 AM10/5/11
to guava-...@googlegroups.com
Hi,

FinalizableReferenceQueue and Finaliz* are deprecated and scheduled for deletion. I understand this is primarily due to MapMaker and friends creating them automatically and then getting lost in webapp classloaders. Fair enough.

I'm using FRQ and FinalizableWeakReference directly for some slightly unpleasant cleanup. Are there plans for any alternative to this, such as passing the FRQ an executor? I think there's still value in the Finalizable*Reference model even if it doesn't work as hoped with an automatic backing thread. Other options are to roll my own or just to use ReferenceQueue directly I suppose.

Thanks
Joe

Charles Fry

unread,
Oct 5, 2011, 8:05:04 AM10/5/11
to Joe Kearney, guava-...@googlegroups.com
The reason we did away with it was because a) the single background thread didn't scale and b) certain environments forbid threads, making FRQ insufficiently general purpose for our needs.

Internally we've just been using ReferenceQueue directly, and we have no plans to introduce other alternatives, though in a limited context you could obviously do something based on FRQ.

Charles

Kevin Bourrillion

unread,
Oct 5, 2011, 10:30:39 AM10/5/11
to Charles Fry, Joe Kearney, guava-...@googlegroups.com
Yeah; if you're convinced you have a good reason to use it, I'd probably just copy it.  I don't think it's a generally good enough idea that we should keep it.
--
Kevin Bourrillion @ Google
Java Core Libraries Team
http://guava-libraries.googlecode.com

Joe Kearney

unread,
Oct 5, 2011, 11:24:36 AM10/5/11
to Kevin Bourrillion, Charles Fry, guava-...@googlegroups.com
Fair enough, I'll do that. Thanks.

Chris Povirk

unread,
Oct 5, 2011, 11:54:03 AM10/5/11
to Joe Kearney, Kevin Bourrillion, Charles Fry, guava-...@googlegroups.com
It's possible that you would want the simplified version from the old
Google Collections:
http://code.google.com/p/google-collections/source/detail?r=69&path=/trunk/src/com/google/common/base/internal/Finalizer.java

It lacks a couple fixes for environments where class unloading is used
and where starting a thread is impossible, but I seem to recall that
problems remained with at least one of those cases, so, especially if
you don't need either support, you might prefer the older version
without the complexity.

Bob Lee

unread,
Oct 5, 2011, 1:52:49 PM10/5/11
to Kevin Bourrillion, Charles Fry, Joe Kearney, guava-...@googlegroups.com
This is disappointing. I just presented FRQ in my JavaOne talk yesterday--it's much nicer to use and more efficient than a vanilla reference queue. MapMaker may not use FRQ, but MapMaker is a special case. MapMaker doesn't use CHM either, but CHM is clearly still valuable.

FRQ has a number of benefits:
  • It provides the convenience of finalizers with almost none of the drawbacks.
  • The generics on ReferenceQueue are broken. It should be ReferenceQueue<T, R extends Reference<T>>, not Reference<T>. The consequence is users need to cast. FRQ fixes this.
  • With traditional reference queues, users have to either hold on to garbage longer than necessary or manage their own threads. FRQ is simple and it proactively cleans up after references.
An FRQ is currently backed by one thread, but the user could easily specify the number of threads they'd like to use, and they could even pass in a ThreadFactory. Some people blamed MapMaker's use of a single background thread on out-of-memory problems, but I'm skeptical that was the actual problem. Unless they had dozens of cores adding entries to maps, it's more likely that they genuinely ran out of memory.

If we're worried about the background thread preventing classes from unloading, just add an explicit shutdown() method like ExecutorService has. We didn't want to add a shutdown() method to MapMaker because it would have exposed an implementation detail, but it's a fine thing to add to FRQ. The reality is this only affects users who want to dynamically reload the Guava classes. The real issue is with Java's Thread impl and could be fixed pretty easily at the JDK level if someone is so inclined. I don't dynamically unload and reload classes, so it's not something I plan to spend time in the near future.

Does Google use ReferenceQueue elsewhere? If so, I'd take another look at those uses and see if they wouldn't benefit from switching to FRQ.

Here's a concrete example from my talk. Using a plain ReferenceQueue:

class NativeMemoryReference
    extends PhantomReference<NativeMemory> {
  final int address;
  NativeMemoryReference(NativeMemory referent,
      ReferenceQueue<NativeMemory> rq) {
    super(referent, rq);
    address = referent.address;
  }
}

public class NativeMemoryManager {
  private static final Set<Reference<?>> refs
      = Collections.synchronizedSet(new HashSet<Reference<?>>());
  private static final ReferenceQueue<NativeMemory> rq
      = new ReferenceQueue<NativeMemory>();
  public static NativeMemory allocate() {
    NativeMemory nm = new NativeMemory();
    refs.add(new NativeMemoryReference(nm, rq));
    cleanUp();
    return nm;
  }
  private static void cleanUp() {
    NativeMemoryReference ref;
    while ((ref = (NativeMemoryReference) rq.poll()) != null) {
      NativeMemory.free(ref.address);
      refs.remove(ref);
    }
  }
}

Here's the same code with FRQ:

public class NativeMemoryManager {
  private static final Set<Reference<?>> refs
      = Collections.synchronizedSet(new HashSet<Reference<?>>());
  private static final FinalizableReferenceQueue frq
      = new FinalizableReferenceQueue();
  public static NativeMemory allocate() {
    NativeMemory nm = new NativeMemory();
    final int address = nm.address;
    refs.add(new FinalizablePhantomReference<NativeMemory>(nm, frq) {
      public void finalizeReferent() {
        NativeMemory.free(address);
        refs.remove(this);
      }
    });
    return nm;
  }
}

The code is simpler, there's no cast, and it frees native memory right away instead of waiting until someone allocates more native memory.

Bob

cowwoc

unread,
Oct 5, 2011, 2:48:03 PM10/5/11
to guava-...@googlegroups.com
Hey Bob,

    I think it makes sense to keep FRQ if the users' legitimate concerns are addressed. Passing in an Executor seems to address both the ability to use multiple threads, and the ability to explicitly shut down. Just a thought...

    I'm sure people would appreciate tutorials and slides demonstrating the usefulness of FRQ and comparing it to traditional methods (finalizers, ReferenceQueue, etc).

Best of luck,
Gili

Kevin Bourrillion

unread,
Oct 6, 2011, 10:02:58 AM10/6/11
to Charles Fry, Joe Kearney, guava-...@googlegroups.com
On Wed, Oct 5, 2011 at 7:30 AM, Kevin Bourrillion <kev...@google.com> wrote:
Yeah; if you're convinced you have a good reason to use it, I'd probably just copy it.  I don't think it's a generally good enough idea that we should keep it.

In fact, I may have been misguided here.  It was mostly only using it in MapMaker that didn't prove to be a good enough idea.

Congratulations folks: you saved a life today. We are going to un-deprecate these classes in the 10.0.1 patch release.

 

cowwoc

unread,
Oct 6, 2011, 10:08:16 AM10/6/11
to guava-...@googlegroups.com

    Does this mean you plan on resolving issues with multiple threads and explicit shutdown...?

Gili

Kevin Bourrillion

unread,
Oct 6, 2011, 10:09:03 AM10/6/11
to cowwoc, guava-...@googlegroups.com
Oh yeah, let me also mention:  BOB'S IN CHARGE.   :-) 

Joe Kearney

unread,
Oct 6, 2011, 11:34:49 AM10/6/11
to Kevin Bourrillion, cowwoc, guava-...@googlegroups.com
Excellent news.

So will this remain with a thread per FRQ, or will it take an executor, or both? Certainly for this limited use I'd be happy with it managing threads automatically.

Thanks

Bob Lee

unread,
Oct 6, 2011, 1:23:51 PM10/6/11
to cowwoc, guava-...@googlegroups.com
On Thu, Oct 6, 2011 at 7:08 AM, cowwoc <cow...@bbs.darktech.org> wrote:
    Does this mean you plan on resolving issues with multiple threads and explicit shutdown...?

Are these issues actually affecting you?

I really don't want to add explicit shutdown support. The thread should shut down just fine on its own unless you couple class unloading with a security manager. The real problem is with Thread's internal AccessControlContext. Someone should submit a patch to OpenJDK.

Do you actually have cleanup logic that needs more than one thread? I suppose we could keep a pool of threads (perhaps each with its own ReferenceQueue) internally. In the mean time, you could just call out to an Executor from finalizeReferent().

Bob



cowwoc

unread,
Oct 6, 2011, 2:00:42 PM10/6/11
to Bob Lee, guava-...@googlegroups.com
Hi Bob,

    All I care about is the ability to reload web applications or load different webapps with different versions of Guice into the same web container. Last I checked, FRQ had problems with this.

    I don't personally need multiple threads, but as previously mentioned, you could solve both problems by allowing users to specify an Executor. Is there a downside to this?

Gili

Colin Decker

unread,
Oct 6, 2011, 2:08:11 PM10/6/11
to cowwoc, guava-...@googlegroups.com
Guice was using FRQ through MapMaker, but MapMaker no longer uses it. I believe the latest Guice source on trunk already uses Guava rather than having its own copies of Guava files, so once that's updated to 10.0 this won't happen in Guice (built from trunk) anymore.

-- 
Colin


--

Bob Lee

unread,
Oct 6, 2011, 2:47:42 PM10/6/11
to cowwoc, guava-...@googlegroups.com
On Thu, Oct 6, 2011 at 11:00 AM, cowwoc <cow...@bbs.darktech.org> wrote:
    All I care about is the ability to reload web applications or load different webapps with different versions of Guice into the same web container. Last I checked, FRQ had problems with this.

Have you checked since we added the code to dynamically load Finalizer? Did you have a security manager installed?
 
    I don't personally need multiple threads, but as previously mentioned, you could solve both problems by allowing users to specify an Executor. Is there a downside to this?

An Executor doesn't really make sense, but a ThreadFactory might. This is pretty far down on my list of priorities.

Bob

Reply all
Reply to author
Forward
0 new messages