-
Notifications
You must be signed in to change notification settings - Fork 721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DTFJ: Finalized objects incorrectly tagged with HEAP_ROOT_FINALIZABLE_OBJ #7091
Comments
@amicic FYI |
@paulcheeseman Can you please share the the output of |
I was testing with IBM Java 8.0.5.37 on Windows:
I just tried to reproduce the issue with a recent Adopt binary but I can't open the resulting dump at all in MAT due to the Error below when parsing - there seems to be some other problem.
|
One minor note on this:
The more fundamental issue is that the objects whose finalizer has already been run are still returned when MAT calls |
Agreed. I wasn't really sure of the mechanics inside DTFJ and MAT, but they shouldn't be described as roots at all once they've been finalized. |
IBM builds use different technology than OpenJ9 to capture the information that becomes the blob in core files: this means you can't expect to use an IBM VM to examine a core file produced from an OpenJ9 VM nor vice-versa. |
Does this mean there are two different DTFJ plugins for MAT? One for IBM Java and one for OpenJ9? |
When I use I think @kgibm is on the right track with the comment that dtfjInfo.getJavaRuntime().getHeapRoots() returns objects it shouldn't. |
I don't know how MAT connects to DTFJ or how its plugin is constructed. |
There's a MAT DTFJ plugin which packages a copy of DTFJ JAR files, so if there is a non-backwards compatible update to DTFJ, then those dumps cannot be parsed. Unfortunately, the build machine that built the MAT DTFJ plugin was sunset so service is difficult. If necessary, I have manually updated the MAT DTFJ plugin like so:
However, this drives some sort of strange issue that I haven't had the time to investigate where there is some race condition and every ~5% of times that MAT is opened, DTFJ is not registered and MAT cannot open PHDs or system dumps (restarting MAT usually solves the issue). Using |
Using the DTFJ plugin available from http://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/runtimes/tools/dtfj/ I'm able to open and read the latest OpenJ9 core files. There was a time period where this didn't work, fixed via #6317 and #6321 for the 0.15 release. If necessary, let's take these concerns to a separate issue if needed, and keep this one about the Finalized object roots. |
I see exactly the same thing with my dumps too. It seems that Here's an MAT screenshot for a dump taken from |
I'm not sure if you all are able to look at Eclipse MAT source (License=EPL1.0), but it calls DTFJ's For any root returned from either case, MAT calls |
If I am reading code correctly it assumes that any instance in the heap of class contain "finalize()" method is treated as alive root. Obviously main concern of developer was "do not miss any potential candidate". So there is no differentiation between "alive" and "dead" objects - no surprise. |
I am not sure how to make correct decision JVM-agnostic. In theory each finalized object has a state:
However different JVMs might have more states or slightly different state machines |
@dmitripivkine An alternative to changing the result of |
As author of the MAT code I think the intention of the code was to cope with DTFJ without accurate finalizer information.
where guessFinalizables is always true, goodDTFJRoots means that some roots were returned from If that wasn't so, then MAT guesses there is no accurate finalizer information from DTFJ and marks unreferenced objects with a finalize() method as Another idea is that DTFJ has the For example:
|
The latest snapshot build of MAT works better. Now if it sees at least one HEAP_ROOT_UNFINALIZED_OBJ or one HEAP_ROOT_UNFINALIZED_OBJ root then it does not guess finalizable roots. This helps for the test program, but running on
the test program still shows 1000 FINALIZABLE roots, because there are no unfinalized or finalizable objects in the dump. MAT does give some warnings in the log: Object of type FinalizerTest at 0x7fff6c3a8 is presumed to be finalizable so has been marked as a GC root Someone has done a good job in removing finalize() from the class libraries! If there is one other object marked as HEAP_ROOT_UNFINALIZED_OBJ or HEAP_ROOT_UNFINALIZED_OBJ then it works as expected and those 1000 are not marked as FINALIZABLE. Also MAT now recognizes HEAP_ROOT_UNFINALIZED_OBJ. Those objects are only weakly retained for finalization and will only be put onto the finalization queue when no longer in use. Adding them as full GC roots is not a good idea. Instead I have added them as references from the java.lang.Runtime object, and the queries which understand weak references also treat these as a type of weak reference. The actual J9 behaviour seems to be:
I now think OpenJ9 DTFJ is behaving as expected. |
I don't understand the table in the comment above: how is the third row meaningful as it appears to refer to an object that returns two distinct values from I would not expect DTFJ to refer to any objects that have been finalized; they're not reachable from any root. |
I was exploring an idea where the object would be a root for two reasons, so would have two JavaReferences. It happens for other root types e.g. HEAP_ROOT_MONITOR and HEAP_ROOT_STACK_LOCAL. Now I don't think it is worth the complication for finalizers. A finalized object could be seen in a heap dump if GC hasn't yet disposed of it. Normally MAT would discard it, but could be seen in the unreachable objects histogram or if keep_unreachable_objects was set. Also a finalizer could choose to make the object ordinarily accessible again; normally this would be a mistake, but I don't know if it happens often enough for it to be useful for MAT to |
Consider the following testcase:
This program creates and discards 1000 objects with finalizers, then forces a GC to induce finalization, and finally triggers a dump so we can examine the state of these objects.
If you run the test and open the resulting dump in Eclipse MAT you will find 1000 FinalizerTest objects of root type
Finalizable
. If you run the "Finalizer Overview" query you can also see that MAT describes the objects as being "ready for finalizer thread".However, if you look at the fields in these objects you can see that they are closed, and
close()
can only be called fromfinalize()
, so the objects have in fact already been finalized. The information presented by MAT is contradictory and misleading.It turns out that MAT is passing on information provided by DTFJ. When parsing system dumps DTFJ tags objects with a
finalize()
method asHEAP_ROOT_FINALIZABLE_OBJ
. MAT then assumes that objects tagged in this way are on the finalizer queue waiting to be finalized. This might be correct, but it might not - objects appear to be tagged in this way by DTFJ regardless of whether or not their finalizer has been run.When finalizable objects have been processed by the finalizer thread they should become unreachable, so they should not be tagged as roots (of any kind) by DTFJ. MAT would then present the objects correctly - i.e. they would be visible in the unreachable objects histogram, or as "roots" of type
Unreachable
if "keep unreachable objects" were enabled when parsing the dump.The text was updated successfully, but these errors were encountered: