RFR(M): JDK-8059036 : Implement Diagnostic Commands for heap and finalizerinfo
Derek White
derek.white at oracle.com
Wed May 6 21:51:42 UTC 2015
Hi Dmitry, Staffan,
Lots of good comments here.
On the topic of what list should be printed out, I think we should focus
on objects waiting to be finalized - e.g. the contents of the
ReferenceQueue. It's more of a pain to walk the ReferenceQueue, but you
could add a summerizeQueue(TreeMap<String, Integer>) method, or a
general iterator and lambda.
A histogram of objects with finalize methods might also be interesting,
but you can get most of the same information from a heap histogram
(ClassHistogramDCmd, and search for count of Finalizer instances).
BTW, by only locking the ReferenceQueue, we should only be blocking the
FinalizerThread from making progress (and I think there's a GC thread
that runs after GC that sorts found References objects that need
processing into their respective ReferenceQueues). But locking the
"unfinalized" list blocks initializing any object with a finalize() method.
The sorting suggestion is a nice touch.
- Derek White, GC team
On 5/5/15 10:40 AM, Peter Levart wrote:
> Hi Dmitry, Staffan,
>
> On 05/05/2015 12:38 PM, Staffan Larsen wrote:
>> Dmitry,
>>
>> I think this should be reviewed on hotspot-gc and core-libs-dev as
>> well considering the changes to Finalizer.
>>
>> I’m a little worried about the potentially very large String that is
>> returned from printFinalizationQueue(). A possible different approach
>> would be to write printFinalizationQueue() in C++ inside Hotspot.
>> That would allow for outputting one line at a time. The output would
>> still be saved in memory (since the stream is buffered), but at least
>> the data is only saved once in memory, then. It would make the code a
>> bit harder to write, so its a question of how scared we are of
>> running out of memory.
>
> If the output is just a histogram of # of instances per class name,
> then it should not be too large, as there are not many different
> classes overriding finalize() method (I counted 72 overriddings of
> finalize() method in the whole jdk/src tree).
>
> I'm more concerned about the fact that while traversing the list, a
> lock is held, which might impact prompt finalization(). Is it
> acceptable for diagnostic output to impact performance and/or
> interfere with synchronization?
>
> It might be possible to scan the list without holding a lock for
> diagnostic purposes if Finalizer.remove() didn't set the removed
> Finalizer.next pointer to point back to itself:
>
> private void remove() {
> synchronized (lock) {
> if (unfinalized == this) {
> if (this.next != null) {
> unfinalized = this.next;
> } else {
> unfinalized = this.prev;
> }
> }
> if (this.next != null) {
> this.next.prev = this.prev;
> }
> if (this.prev != null) {
> this.prev.next = this.next;
> }
> // this.next = this; must not be set so that we can
> traverse the list unsynchronized
> this.prev = this; /* Indicates that this has been
> finalized */
> }
> }
>
> For detecting whether a Finalizer is already processed, the 'prev'
> pointer could be used instead:
>
> private boolean hasBeenFinalized() {
> return (prev == this);
> }
>
> Also, to make sure a data race dereferencing 'unfinalized' in
> unsynchronized printFinalizationQueue() would get you a fully
> initialized Finalizer instance (in particular the next pointer), you
> would have to make the 'unfinalized' field volatile or insert an
> Unsafe.storeFence() before assigning to 'unfinalized':
>
> private void add() {
> synchronized (lock) {
> if (unfinalized != null) {
> this.next = unfinalized;
> unfinalized.prev = this;
> }
> // make sure a data race dereferencing 'unfinalized'
> // in printFinalizationQueue() does see the Finalizer
> // instance fully initialized
> // (in particular the 'next' pointer)
> U.storeFence();
> unfinalized = this;
> }
> }
>
>
> By doing these modifications, I think you can remove
> synchronized(lock) {} from printFinalizationQueue().
>
>>
>> I see you are traversing the ‘unfinalized’ list in Finalizer, whereas
>> the old SA code for ‘-finalizerinfo' traverses the ‘queue’ list. I am
>> not sure of the difference, perhaps someone from the GC team can help
>> out?
>
> The 'queue' is a ReferenceQueue of Finalizer (FinalReference)
> instances pending processing by finalizer thread because their
> referents are eligible for finalization (they are not reachable any
> more). The 'unfinalized' is a doubly-linked list of all Finalizer
> instances for which their referents have not been finalized yet
> (including those that are still reachable and alive). The later serves
> two purposes:
> - it keeps Finalizer instances reachable until they are processed
> - it is a source of unfinalized instances for
> running-finalizers-on-exit if requested by
> System.runFinalizersOnExit(true);
>
> So it really depends on what one would like to see. Showing the queue
> may be interesting if one wants to see how the finalizer thread is
> coping with processing the finalize() invocations. Showing unfinalized
> list may be interesting if one wants to know how many live +
> finalization pending instances are there on the heap that override
> finalize() method.
>
> Regards, Peter
>
>>
>> For the output, it would be a nice touch to sort it on the number of
>> references for each type. Perhaps outputting it more like a table
>> (see the old code again) would also make it easier to digest.
>>
>> In diagnosticCommand.hpp, the new commands should override
>> permission() and limit access:
>>
>> static const JavaPermission permission() {
>> JavaPermission p = {"java.lang.management.ManagementPermission",
>> "monitor", NULL};
>> return p;
>> }
>>
>> The two tests don’t validate the output in any way. Would it be
>> possible to add validation? Perhaps hard to make sure an object is on
>> the finalizer queue… Hmm.
>>
>> Thanks,
>> /Staffan
>>
>>
>>> On 5 maj 2015, at 12:01, Dmitry Samersoff
>>> <dmitry.samersoff at oracle.com> wrote:
>>>
>>> Everyone,
>>>
>>> Please review the fix:
>>>
>>> http://cr.openjdk.java.net/~dsamersoff/JDK-8059036/webrev.01/
>>>
>>> heap dcmd outputs the same information as SIGBREAK
>>>
>>> finalizerinfo dcmd outputs a list of all classes in finalization queue
>>> with count
>>>
>>> -Dmitry
>>>
>>> --
>>> Dmitry Samersoff
>>> Oracle Java development team, Saint Petersburg, Russia
>>> * I would love to change the world, but they won't give me the sources.
>
More information about the serviceability-dev
mailing list