/** 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();
    doJNIHandleBlock(handles.globalHandles(), new RootVisitor("Global JNI handle root"));
    doJNIHandleBlock(handles.weakGlobalHandles(), new RootVisitor("Weak global JNI handle root"));

    // Do Java-level static fields
    SystemDictionary sysDict = VM.getVM().getSystemDictionary();
    sysDict.allClassesDo(
        new SystemDictionary.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;
  }