What is the up-front cost of an object being finalizable?
Asked Answered
D

1

9

Discussions of finalizable objects in Java typically discuss the common indirect costs that happen when finalizable objects (and their associated resources) cannot be quickly garbage collected.

I'm more interested, at the moment, in what the actual direct cost of being finalizable is, both in memory terms, and in object allocation time. I've seen oblique references to the existence of such a cost in a number of places, for example, Oracle's article on finalization memory retention issues notes:

When obj is allocated, the JVM internally records that obj is finalizable. This typically slows down the otherwise fast allocation path that modern JVMs have.

How does the JVM record that an object instance is finalizable, and what are the memory and performance costs of doing so?

For those interested in my specific application:

We produce and retain millions of incredibly lightweight objects; adding a single pointer to these objects is incredibly costly, so we've done a fair bit of work to remove pointers from them, instead using smaller numeric ids packed into a subset of the bits of a field. Unpacking the number allows the shared immutable property with that id to be retrieved from a Pool that stores them using a Map.

The remaining question is how to handle the garbage collection of property values that are no longer used.

One strategy that has been considered is using reference counting; when objects are created and retrieve the pooled id for a value, the reference count for that value is incremented; when it is no longer used, it must be decremented.

One option to ensure this decrement happens is to add the following finalize method:

public void finalize() {
    Pool.release(getPropertyId());
}

However, if the very act of being finalizable means that an additional pointer to the object must be kept, the up-front cost of being finalizable would be considered high for this application. If it means additional objects must be allocated, it would almost certainly be too high...hence, my question: what is the direct up-front cost of being finalizable?

Doily answered 8/5, 2015 at 19:0 Comment(14)
Isn't it bad practise to rely on finalization of objects? i mean, it's never guarantee finalize() will be called - or?Membranous
You might want to manage your own object pool rather than leaning on the JVM. Depending on how many objects you need simultaneously, you may find a huge performance gain by completely avoiding the allocation/garbage collection/finalization overhead.Lax
You should never be creating so many finalizable objects that this should be a concern. You should only use it as a last resort or you will see a serious performance / stability problem when the objects are cleaned. You might not be concerned about it not, but use this a lot and you will be.Sorbose
Can you use a reference queue instead of finalization? See e.g. https://mcmap.net/q/474157/-using-java-39-s-referencequeue for how this might work.Output
@LouisWasserman I could, but I'm fairly certain that allocating another object to keep track of whether one of my lightweight objects has been deleted will be as high or higher cost than using finalizable in terms of memory. Doing so with phantom references would be better in terms of not blocking garbage collection, if finalization is too slow, and my own queue running at a higher priority might work faster, but allocating another object that requires similar note taking by the JVM likely has an even higher up-front cost than being finalizable.Doily
@TheodoreMurdock: Generally you have one reference queue for the entire application. Reference queues are generally considered significantly safer and more efficient than finalization.Output
@PeterLawrey I imagine that might be the case. However, these objects tend to have very long lifetimes (measured in hours). Usage patterns may vary, a user can ask for the entire database of around 20 million objects to be rebuilt, but more commonly only a few thousand would be lost and rebuilt at a time. Since a few references to a small percentage of the deleted objects might be retained, we'd like for their properties to also remain retrievable.Doily
@LouisWasserman Sure, there's just one queue, but there are n weak or transient reference objects if there are n objects to do cleanup for, and the JVM must take notes on each such weak reference so they can be enqueued when the corresponding object is garbage collected.Doily
Usually what you'll do is make one of your classes extend WeakReference, so you don't have any extra objects. But even so, that extra information is generally considered significantly cheaper than finalization.Output
@LouisWasserman ...because people are concerned with the potential for memory retention due to finalization not happening quickly. I've just run a quick test, it appears that, in my case, all 20 million objects are finalized and garbage collected very quickly with no fuss when the database must be rebuilt. So the only real concern I have is the upfront cost.Doily
@TheodoreMurdock Quite the opposite. Finalizers are more expensive than PhantomReferences. They are also backed by an instance of java.lang.ref.Reference (FinalReference), so they can't be cheaper, but their behavior is more complicated. Moreover, HotSpot JVM does not inline allocation of objects that have a non-trivial finalizer.Perfectible
@Perfectible If you know the answer, would you consider answering my question?Doily
@Perfectible Also, is the implementation really lighter weight for PhantomReferences? The PhantomReference must be placed into a specific queue so the JVM must know a triplet of information: (the phantom reference, the queue to place it in, the object that signals that the phantom reference should be placed in the queue). Is that triplet of information really retained at a lower memory cost than the simple knowledge that the finalize method of an object must be run before it can be garbage collected? If so, how?Doily
Actually, I should modify my reply to @LouisWasserman...after rebuilding the database a few times the memory did seem to build up due to lack of finalization; it appears I had overestimated how much of the memory was due to these small objects (and not their contents) so my initial testing underestimated the effects. At any rate, with the answer being that a much larger object must be allocated to keep track of a simple finalizable object, the up-front memory cost is too high for my application regardless of the performance of finalization, as I suspected it might be.Doily
P
8

Finalizers are awful not only because of retention issues, but from performance perspective, too.

In Oracle JDK / OpenJDK the objects with finalize method are backed by the instances of Finalizer, the subclass of java.lang.ref.Reference.

All Finalizers are registered at the end of object's contructor in two steps: a call from Java to VM followed by the invocation of Finalizer.register(). This double transition Java->VM->Java cannot be inlined by JIT compiler. But the worst thing is that Finalizer's constructor makes a linked list under the global lock! (facepalm)

Finalizers are also bad in terms of memory footprint: in addition to all Reference fields they have two extra fields: next and prev.

PhantomReferences are much better than finalizers:

  • their construction does not require transition to VM and back and can be inlined;
  • they do not have extra fields besides inherited from java.lang.ref.Reference;
  • no global synchronization is made.

This benchmark compares the allocation speed of finalizable objects and the objects backed by PhantomReference:

Benchmark               Mode  Cnt       Score      Error   Units
Finalizer.finalizable  thrpt    5    2171,312 ± 1469,705  ops/ms
Finalizer.phantom      thrpt    5   61280,612 ±  692,922  ops/ms
Finalizer.plain        thrpt    5  225752,307 ± 7618,304  ops/ms
Perfectible answered 8/5, 2015 at 21:12 Comment(2)
Is there any reason for not patching global lock in openJDK?Bonspiel
@Bonspiel I'd say there is no reason for patching. Why care about legacy code that is not recommended to use anyway.Perfectible

© 2022 - 2024 — McMap. All rights reserved.