/*
 * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

package sun.jvm.hotspot.utilities;

import java.io.*;
import java.util.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.classfile.*;
import sun.jvm.hotspot.gc.shared.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.oops.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.utilities.*;

For a set of known roots, descends recursively into the object graph, for each object recording those objects (and their fields) which point to it. NOTE: currently only a subset of the roots known to the VM is exposed to the SA: objects on the stack, static fields in classes, and JNI handles. These should be most of the user-level roots keeping objects alive.
/** For a set of known roots, descends recursively into the object graph, for each object recording those objects (and their fields) which point to it. NOTE: currently only a subset of the roots known to the VM is exposed to the SA: objects on the stack, static fields in classes, and JNI handles. These should be most of the user-level roots keeping objects alive. */
public class ReversePtrsAnalysis { // Used for debugging this code private static final boolean DEBUG = false; public ReversePtrsAnalysis() { }
Sets an optional progress thunk
/** Sets an optional progress thunk */
public void setHeapProgressThunk(HeapProgressThunk thunk) { progressThunk = thunk; }
Runs the analysis algorithm
/** Runs the analysis algorithm */
public void run() { if (VM.getVM().getRevPtrs() != null) { return; // Assume already done } VM vm = VM.getVM(); rp = new ReversePtrs(); vm.setRevPtrs(rp); Universe universe = vm.getUniverse(); CollectedHeap collHeap = universe.heap(); usedSize = collHeap.used(); visitedSize = 0; // Note that an experiment to iterate the heap linearly rather // than in recursive-descent order has been done. It turns out // that the recursive-descent algorithm is nearly twice as fast // due to the fact that it scans only live objects and (currently) // only a fraction of the perm gen, namely the static fields // contained in instanceKlasses. (Iterating the heap linearly // would also change the semantics of the result so that // ReversePtrs.get() would return a non-null value even for dead // objects.) Nonetheless, the reverse pointer computation is still // quite slow and optimization in field iteration of objects // should be done. if (progressThunk != null) { // Get it started progressThunk.heapIterationFractionUpdate(0); } // Allocate mark bits for heap markBits = new MarkBits(collHeap); // Get a hold of the object heap heap = vm.getObjectHeap(); // Do each thread's roots for (JavaThread thread = VM.getVM().getThreads().first(); thread != null; thread = thread.next()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); String threadDesc = " in thread \"" + thread.getThreadName() + "\" (id " + bos.toString() + ")"; doStack(thread, new RootVisitor("Stack root" + threadDesc)); doJNIHandleBlock(thread.activeHandles(), new RootVisitor("JNI handle root" + threadDesc)); } // Do global JNI handles JNIHandles handles = VM.getVM().getJNIHandles(); doOopStorage(handles.globalHandles(), new RootVisitor("Global JNI handle root")); doOopStorage(handles.weakGlobalHandles(), new RootVisitor("Weak global JNI handle root")); // Do Java-level static fields ClassLoaderDataGraph cldg = VM.getVM().getClassLoaderDataGraph(); cldg.classesDo(new ClassLoaderDataGraph.ClassVisitor() { public void visit(Klass k) { if (k instanceof InstanceKlass) { final InstanceKlass ik = (InstanceKlass)k; ik.iterateStaticFields( new DefaultOopVisitor() { public void doOop(OopField field, boolean isVMField) { Oop next = field.getValue(getObj()); NamedFieldIdentifier nfi = new NamedFieldIdentifier("Static field \"" + field.getID().getName() + "\" in class \"" + ik.getName().asString() + "\""); LivenessPathElement lp = new LivenessPathElement(null, nfi); rp.put(lp, next); try { markAndTraverse(next); } catch (AddressException e) { System.err.print("RevPtrs analysis: WARNING: AddressException at 0x" + Long.toHexString(e.getAddress()) + " while traversing static fields of InstanceKlass "); ik.printValueOn(System.err); System.err.println(); } catch (UnknownOopException e) { System.err.println("RevPtrs analysis: WARNING: UnknownOopException while " + "traversing static fields of InstanceKlass "); ik.printValueOn(System.err); System.err.println(); } } }); } } }); if (progressThunk != null) { progressThunk.heapIterationComplete(); } // Clear out markBits markBits = null; } //--------------------------------------------------------------------------- // Internals only below this point // private HeapProgressThunk progressThunk; private long usedSize; private long visitedSize; private double lastNotificationFraction; private static final double MINIMUM_NOTIFICATION_FRACTION = 0.01; private ObjectHeap heap; private MarkBits markBits; private int depth; // Debugging only private ReversePtrs rp; private void markAndTraverse(OopHandle handle) { try { markAndTraverse(heap.newOop(handle)); } catch (AddressException e) { System.err.println("RevPtrs analysis: WARNING: AddressException at 0x" + Long.toHexString(e.getAddress()) + " while traversing oop at " + handle); } catch (UnknownOopException e) { System.err.println("RevPtrs analysis: WARNING: UnknownOopException for " + "oop at " + handle); } } private void printHeader() { for (int i = 0; i < depth; i++) { System.err.print(" "); } } private void markAndTraverse(final Oop obj) { // End of path if (obj == null) { return; } // Visited object if (!markBits.mark(obj)) { return; } // Root of work list for objects to be visited. A simple // stack for saving new objects to be analyzed. final Stack workList = new Stack(); // Next object to be visited. Oop next = obj; try { // Node in the list currently being visited. while (true) { final Oop currObj = next; // For the progress meter if (progressThunk != null) { visitedSize += currObj.getObjectSize(); double curFrac = (double) visitedSize / (double) usedSize; if (curFrac > lastNotificationFraction + MINIMUM_NOTIFICATION_FRACTION) { progressThunk.heapIterationFractionUpdate(curFrac); lastNotificationFraction = curFrac; } } if (DEBUG) { ++depth; printHeader(); System.err.println("ReversePtrs.markAndTraverse(" + currObj.getHandle() + ")"); } // Iterate over the references in the object. Do the // reverse pointer analysis for each reference. // Add the reference to the work-list so that its // references will be visited. currObj.iterate(new DefaultOopVisitor() { public void doOop(OopField field, boolean isVMField) { // "field" refers to a reference in currObj Oop next = field.getValue(currObj); rp.put(new LivenessPathElement(currObj, field.getID()), next); if ((next != null) && markBits.mark(next)) { workList.push(next); } } }, false); if (DEBUG) { --depth; } // Get the next object to visit. next = (Oop) workList.pop(); } } catch (EmptyStackException e) { // Done } catch (NullPointerException e) { System.err.println("ReversePtrs: WARNING: " + e + " during traversal"); } catch (Exception e) { System.err.println("ReversePtrs: WARNING: " + e + " during traversal"); } } class RootVisitor implements AddressVisitor { RootVisitor(String baseRootDescription) { this.baseRootDescription = baseRootDescription; } public void visitAddress(Address addr) { Oop next = heap.newOop(addr.getOopHandleAt(0)); LivenessPathElement lp = new LivenessPathElement(null, new NamedFieldIdentifier(baseRootDescription + " @ " + addr)); rp.put(lp, next); markAndTraverse(next); } public void visitCompOopAddress(Address addr) { Oop next = heap.newOop(addr.getCompOopHandleAt(0)); LivenessPathElement lp = new LivenessPathElement(null, new NamedFieldIdentifier(baseRootDescription + " @ " + addr)); rp.put(lp, next); markAndTraverse(next); } private String baseRootDescription; } // Traverse the roots on a given thread's stack private void doStack(JavaThread thread, AddressVisitor oopVisitor) { for (StackFrameStream fst = new StackFrameStream(thread); !fst.isDone(); fst.next()) { fst.getCurrent().oopsDo(oopVisitor, fst.getRegisterMap()); } } // Traverse a JNIHandleBlock private void doJNIHandleBlock(JNIHandleBlock handles, AddressVisitor oopVisitor) { handles.oopsDo(oopVisitor); } // Traverse jobjects in global JNIHandles private void doOopStorage(OopStorage oopSet, AddressVisitor oopVisitor) { oopSet.oopsDo(oopVisitor); } }