PhantomReferences are a way for a Java application to perform post-mortem cleanup (using fields in a subclass of PhantomReference rather than the referent) as opposed to finalizers that are pre-mortem. Java 9 added Cleaner which is an easier way to work with PhantomReferences. As with finalizers (which are now deprecated), one of the problems with PhantomReferences & Cleaners is that their clean-up is non-deterministic and may be single-threaded. This can cause Java heap memory pressure, garbage collection performance problems, thrashing, and more. This pressure is significantly reduced due to a recent change in the Java specification that releases the referent memory earlier (available since IBM Java 22.214.171.124 & OpenJ9 0.35).
Although rare, there are cases of terrible performance caused by the rate of PhantomReference creation exceeding the rate of cleanup. This can lead to garbage collection thrashing as memory pressure builds up. A manually taken core dump during such thrashing may be loaded into the Eclipse Memory Analyzer Tool (MAT) and the rest of this post explains how to use MAT to understand if PhantomReferences are the proximate cause of the problem. This would normally be investigated when there are no other obvious causes of the memory pressure.
How much is retained by PhantomReferences
The first question is whether or not the referents of PhantomReferences retain a large proportion of the Java heap.
In MAT, click Open Query Browser } Java Basics } References } Phantom Reference Statistics
When finished, MAT will open three result views:
- Histogram of Phantomly Referenced
- Only Phantomly Referenced
- Referents strongly retained by phantom references
In the first view (Histogram of Phantomly Referenced), select the first class row and Shift+select to the last "Total" row } Right Click } Show Retained Set } Finish. In the resulting view, scroll to the bottom and the Total row's value for Shallow Heap is the total amount retained by PhantomReference referents. Note that this doesn't mean that this much memory is only phantomly reachable, but it gives you a feeling of the volume of memory retained by PhantomReferences. If this is not a large proportion of the Java heap, then you may conclude that PhantomReferences are not the proximate issue. If this is a large proportion, then continue with the next steps.
Understanding retained heap by referent class
In the first view } Click the Calculator icon } Calculate Minimum Retained Size (quick approx.) } Sort by the new "Retained Heap" column. This gives a basic view on which classes are consuming how much of the volume of PhantomReferences. Note that these rows are not necessarily mutually exclusive.
Understanding what's only phantomly reachable
In the first view, select the first class row and Shift+select to the last "Total" row, or select the classes of interest from the last step, and then right click } Merge Shortest Paths to GC Roots } exclude all phantom/weak/soft etc. references. In the resulting view, click the calculator icon } Referenced Objects } Calculate Minimum Retained Size (quick approx.).
If the difference between the retained heap of this view is very large compared to the retained heaps in the first results above, then you've found only phantomly reachable objects that have not yet been processed and thus putting memory pressure on the heap. The next step would be to work with the owners of the PhantomReference classes and their referents to understand who's creating them and whether that can be optimized (and to ensure you've upgraded to a version of Java that clears the referent as mentioned in the introduction).
PhantomReferences are one of the most complicated causes of Java heap memory pressure. Hopefully you'll never have to face such a memory dump; but, if you do, the above steps should help you understand whether or not PhantomReferences are the proximate issue, and which classes need to be investigated.
#automation-portfolio-specialists-app-platform #WebSphereLiberty #java #WebSphereApplicationServer