Index: build.xml
===================================================================
--- build.xml	(revision 14341)
+++ build.xml	(working copy)
@@ -1137,7 +1137,6 @@
       <sysproperty key="mmtk.properties" value="${mmtk.properties}"/>
       <sysproperty key="rvm.properties" value="${build.base}/rvm.properties"/>
       <jvmarg value="-Xbootclasspath/a:${rvm.class.path}"/>
-      <jvmarg value="-Xss2m"/>
       <arg value="-log"/>
       <arg value="${build.base}/BootImageWriterOutput.txt"/>
       <arg value="-classpath"/>
Index: tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageWriterConstants.java
===================================================================
--- tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageWriterConstants.java	(revision 14341)
+++ tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageWriterConstants.java	(working copy)
@@ -33,6 +33,11 @@
   Address OBJECT_NOT_PRESENT = Address.fromIntSignExtend(0xeeeeeee2);
 
   /**
+   * Address to associate with objects that have their allocation deferred.
+   */
+  Address OBJECT_ALLOCATION_DEFERRED = Address.fromIntSignExtend(0xeeeeeee3);
+
+  /**
    * Starting index for objects in VM_TypeDictionary.
    * = 1, since slot 0 is reserved for null
    */
Index: tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageWriter.java
===================================================================
--- tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageWriter.java	(revision 14341)
+++ tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageWriter.java	(working copy)
@@ -12,16 +12,21 @@
  */
 package org.jikesrvm.tools.bootImageWriter;
 
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.PriorityQueue;
+import java.util.Queue;
 import java.util.SortedSet;
-import java.util.Iterator;
+import java.util.Stack;
 import java.util.TreeSet;
 import java.util.Vector;
-import java.util.Stack;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Arrays;
-import java.util.BitSet;
 import java.util.concurrent.*;
 
 import java.io.*;
@@ -30,8 +35,6 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.Comparator;
-import java.util.HashSet;
 
 import org.jikesrvm.*;
 import org.jikesrvm.compilers.common.VM_CompiledMethod;
@@ -138,7 +141,7 @@
    */
   private static class FieldInfo {
     /**
-     *  Field table from JDK verion of class
+     *  Field table from JDK version of class
      */
     final Field[]  jdkFields;
 
@@ -161,13 +164,13 @@
     /**
      *  Jdk type associated with this Field info
      */
-    final Class jdkType;
+    final Class<?> jdkType;
 
     /**
      * Constructor.
      * @param jdkType the type to associate with the key
      */
-    public FieldInfo(Class jdkType, VM_Type rvmType) {
+    public FieldInfo(Class<?> jdkType, VM_Type rvmType) {
       this.jdkFields = jdkType.getDeclaredFields();
       this.jdkType = jdkType;
       this.rvmType = rvmType;
@@ -181,19 +184,19 @@
     /**
      * Jdk type
      */
-    final Class jdkType;
+    final Class<?> jdkType;
 
     /**
      * Constructor.
      * @param jdkType the type to associate with the key
      */
-    public Key(Class jdkType) { this.jdkType = jdkType; }
+    public Key(Class<?> jdkType) { this.jdkType = jdkType; }
 
     /**
      * Returns a hash code value for the key.
      * @return a hash code value for this key
      */
-    public int hashCode() { return System.identityHashCode(jdkType); }
+    public int hashCode() { return jdkType.hashCode(); }
 
     /**
      * Indicates whether some other key is "equal to" this one.
@@ -206,6 +209,225 @@
     }
   }
 
+  /**
+   * Comparator that always says entries are equivalent. For use when
+   * comparator defers to another comparator.
+   */
+  private static final class IdenticalComparator implements Comparator<BootImageMap.Entry> {
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      return 0;
+    }
+  }
+
+  /**
+   * Comparator of boot image entries that sorts according to the type
+   * reference ID.
+   */
+  private static final class TypeReferenceComparator implements Comparator<BootImageMap.Entry> {
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      VM_TypeReference aRef = VM_TypeReference.findOrCreate(a.jdkObject.getClass());
+      VM_TypeReference bRef = VM_TypeReference.findOrCreate(b.jdkObject.getClass());
+      return aRef.getId() - bRef.getId();
+    }
+  }
+
+  /**
+   * Comparator of boot image entries that sorts according to the name of the
+   * classes.
+   */
+  private static final class ClassNameComparator implements Comparator<BootImageMap.Entry> {
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      return -a.jdkObject.getClass().toString().compareTo(b.jdkObject.getClass().toString());
+    }
+  }
+
+  /**
+   * Comparator of boot image entries that sorts according to the size of
+   * the objects.
+   */
+  private static final class ObjectSizeComparator implements Comparator<BootImageMap.Entry> {
+    private final Comparator<BootImageMap.Entry> identicalSizeComparator;
+    ObjectSizeComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
+      this.identicalSizeComparator = identicalSizeComparator;
+    }
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      VM_TypeReference aRef = VM_TypeReference.findOrCreate(a.jdkObject.getClass());
+      VM_TypeReference bRef = VM_TypeReference.findOrCreate(b.jdkObject.getClass());
+      if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
+        return identicalSizeComparator.compare(a, b);
+      } else if (!aRef.isResolved()) {
+        return -1;
+      } else if (!bRef.isResolved()) {
+        return 1;
+      } else {
+        int aSize = getSize(aRef.peekType(), a.jdkObject);
+        int bSize = getSize(bRef.peekType(), b.jdkObject);
+        if (aSize == bSize) {
+          return identicalSizeComparator.compare(a, b);
+        } else {
+          return aSize - bSize;
+        }
+      }
+    }
+  }
+
+  /**
+   * Comparator of boot image entries that sorts according to the number of
+   * references within the objects.
+   */
+  private static final class NumberOfReferencesComparator implements Comparator<BootImageMap.Entry> {
+    private final Comparator<BootImageMap.Entry> identicalSizeComparator;
+    NumberOfReferencesComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
+      this.identicalSizeComparator = identicalSizeComparator;
+    }
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      VM_TypeReference aRef = VM_TypeReference.findOrCreate(a.jdkObject.getClass());
+      VM_TypeReference bRef = VM_TypeReference.findOrCreate(b.jdkObject.getClass());
+      if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
+        return identicalSizeComparator.compare(a, b);
+      } else if (!aRef.isResolved()) {
+        return 1;
+      } else if (!bRef.isResolved()) {
+        return -1;
+      } else {
+        int aSize = getNumberOfReferences(aRef.peekType(), a.jdkObject);
+        int bSize = getNumberOfReferences(bRef.peekType(), b.jdkObject);
+        if (aSize == bSize) {
+          return identicalSizeComparator.compare(a, b);
+        } else {
+          return bSize - aSize;
+        }
+      }
+    }
+  }
+
+  /**
+   * Comparator of boot image entries that sorts according to the number of
+   * non-final references within the objects.
+   */
+  private static final class NumberOfNonFinalReferencesComparator implements Comparator<BootImageMap.Entry> {
+    private final Comparator<BootImageMap.Entry> identicalSizeComparator;
+    NumberOfNonFinalReferencesComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
+      this.identicalSizeComparator = identicalSizeComparator;
+    }
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      VM_TypeReference aRef = VM_TypeReference.findOrCreate(a.jdkObject.getClass());
+      VM_TypeReference bRef = VM_TypeReference.findOrCreate(b.jdkObject.getClass());
+      if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
+        return identicalSizeComparator.compare(a, b);
+      } else if (!aRef.isResolved()) {
+        return 1;
+      } else if (!bRef.isResolved()) {
+        return -1;
+      } else {
+        int aSize = getNumberOfNonFinalReferences(aRef.peekType(), a.jdkObject);
+        int bSize = getNumberOfNonFinalReferences(bRef.peekType(), b.jdkObject);
+        if (aSize == bSize) {
+          return identicalSizeComparator.compare(a, b);
+        } else {
+          return bSize - aSize;
+        }
+      }
+    }
+  }
+
+  /**
+   * Comparator of boot image entries that sorts according to the density of
+   * non-final references within the objects.
+   */
+  private static final class NonFinalReferenceDensityComparator implements Comparator<BootImageMap.Entry> {
+    private final Comparator<BootImageMap.Entry> identicalSizeComparator;
+    NonFinalReferenceDensityComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
+      this.identicalSizeComparator = identicalSizeComparator;
+    }
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      VM_TypeReference aRef = VM_TypeReference.findOrCreate(a.jdkObject.getClass());
+      VM_TypeReference bRef = VM_TypeReference.findOrCreate(b.jdkObject.getClass());
+      if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
+        return identicalSizeComparator.compare(a, b);
+      } else if (!aRef.isResolved()) {
+        return 1;
+      } else if (!bRef.isResolved()) {
+        return -1;
+      } else {
+        double aSize = (double)getNumberOfNonFinalReferences(aRef.peekType(), a.jdkObject) / (double)getSize(aRef.peekType(), a.jdkObject);
+        double bSize = (double)getNumberOfNonFinalReferences(bRef.peekType(), b.jdkObject) / (double)getSize(bRef.peekType(), b.jdkObject);
+        int result = Double.compare(aSize, bSize);
+        if (result == 0) {
+          return identicalSizeComparator.compare(a, b);
+        } else {
+          return -result;
+        }
+      }
+    }
+  }
+
+  /**
+   * Comparator of boot image entries that sorts according to the density of
+   * references within the objects.
+   */
+  private static final class ReferenceDensityComparator implements Comparator<BootImageMap.Entry> {
+    private final Comparator<BootImageMap.Entry> identicalSizeComparator;
+    ReferenceDensityComparator(Comparator<BootImageMap.Entry> identicalSizeComparator) {
+      this.identicalSizeComparator = identicalSizeComparator;
+    }
+    public int compare(BootImageMap.Entry a, BootImageMap.Entry b) {
+      VM_TypeReference aRef = VM_TypeReference.findOrCreate(a.jdkObject.getClass());
+      VM_TypeReference bRef = VM_TypeReference.findOrCreate(b.jdkObject.getClass());
+      if ((!aRef.isResolved() && !aRef.isResolved()) || (aRef == bRef)) {
+        return identicalSizeComparator.compare(a, b);
+      } else if (!aRef.isResolved()) {
+        return 1;
+      } else if (!bRef.isResolved()) {
+        return -1;
+      } else {
+        double aSize = (double)getNumberOfReferences(aRef.peekType(), a.jdkObject) / (double)getSize(aRef.peekType(), a.jdkObject);
+        double bSize = (double)getNumberOfReferences(bRef.peekType(), b.jdkObject) / (double)getSize(bRef.peekType(), b.jdkObject);
+        int result = Double.compare(aSize, bSize);
+        if (result == 0) {
+          return identicalSizeComparator.compare(a, b);
+        } else {
+          return -result;
+        }
+      }
+    }
+  }
+
+  /**
+   * Entries yet to be written into the boot image
+   */
+  private static final Queue<BootImageMap.Entry> pendingEntries;
+  static {
+    if (true) // depth first traversal
+      pendingEntries = new LinkedList<BootImageMap.Entry>() {
+        public BootImageMap.Entry remove() {
+          return removeLast();
+        }};
+    else if (false) pendingEntries = new LinkedList<BootImageMap.Entry>(); // breadth first traversal
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11, new TypeReferenceComparator());
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11, new ClassNameComparator());
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new ObjectSizeComparator(new IdenticalComparator()));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new NumberOfReferencesComparator(new IdenticalComparator()));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new NumberOfNonFinalReferencesComparator(new IdenticalComparator()));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new NumberOfNonFinalReferencesComparator(new ObjectSizeComparator(new TypeReferenceComparator())));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new NumberOfReferencesComparator(new ObjectSizeComparator(new ClassNameComparator())));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new NumberOfReferencesComparator(new ObjectSizeComparator(new TypeReferenceComparator())));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new ReferenceDensityComparator(new IdenticalComparator()));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new NonFinalReferenceDensityComparator(new IdenticalComparator()));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new ReferenceDensityComparator(new TypeReferenceComparator()));
+    else if (false) pendingEntries = new PriorityQueue<BootImageMap.Entry>(11,
+        new NonFinalReferenceDensityComparator(new TypeReferenceComparator()));
+  }
+
   private static final boolean STATIC_FIELD = true;
   private static final boolean INSTANCE_FIELD = false;
 
@@ -259,12 +481,6 @@
   private static boolean profile = false;
 
   /**
-   * What is the threshold (in ms) for compilation of a single class
-   * to be reported as excessively long (when profiling is true)
-   */
-  private static final int classCompileThreshold = 5000;
-
-  /**
    * A wrapper around the calling context to aid in tracing.
    */
   private static class TraceContext extends Stack<String> {
@@ -707,9 +923,10 @@
     if (verbose >= 1) say("copying statics");
     try {
       int refSlotSize = VM_Statics.getReferenceSlotSize();
+      //copyToBootImage(VM_Statics.getSlotsAsIntArray(), false, Address.max(), null, false);
       for (int i = VM_Statics.middleOfTable+refSlotSize, n = VM_Statics.getHighestInUseSlot();
-           i <= n;
-           i+= refSlotSize) {
+      i <= n;
+      i+= refSlotSize) {
         if(!VM_Statics.isReference(i)) {
           throw new Error("Static " + i + " of " + n + " isn't reference");
         }
@@ -724,17 +941,20 @@
           continue;
 
         if (verbose >= 2) traceContext.push(jdkObject.getClass().getName(),
-                                            getRvmStaticField(jtocOff) + "");
-        Address imageAddress = copyToBootImage(jdkObject, false, Address.max(), VM_Statics.getSlotsAsIntArray(), false);
-        if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
-          // object not part of bootimage: install null reference
-          if (verbose >= 2) traceContext.traceObjectNotInBootImage();
-          bootImage.setNullAddressWord(jtocPtr.plus(jtocOff), false, false, false);
-        } else {
-          bootImage.setAddressWord(jtocPtr.plus(jtocOff), imageAddress.toWord(), false, false);
-        }
+            getRvmStaticField(jtocOff) + "");
+        copyReferenceFieldToBootImage(jtocPtr.plus(jtocOff), jdkObject, VM_Statics.getSlotsAsIntArray(), false, false, null, null);
         if (verbose >= 2) traceContext.pop();
       }
+      // Copy entries that are in the pending queue
+      processPendingEntries();
+      // Find and copy unallocated entries
+      for (int i=0; i < BootImageMap.objectIdToEntry.size(); i++) {
+        BootImageMap.Entry mapEntry = BootImageMap.objectIdToEntry.get(i);
+        if (mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
+          mapEntry.imageAddress = copyToBootImage(mapEntry.jdkObject, false, Address.max(), null, false);
+          fixupLinkAddresses(mapEntry);
+        }
+      }
     } catch (IllegalAccessException e) {
       fail("unable to copy statics: "+e);
     }
@@ -775,6 +995,8 @@
         VM.sysWriteln("newBootRecordImageOffset = ", newBootRecordImageAddress);
         VM._assert(newBootRecordImageAddress.EQ(bootRecordImageAddress));
       }
+      // Make sure pending entries are fully written out
+      processPendingEntries();
     } catch (IllegalAccessException e) {
       fail("unable to update boot record: "+e);
     }
@@ -1166,7 +1388,7 @@
         if (!rvmType.isClassType())
           continue; // arrays and primitives have no static or instance fields
 
-        Class jdkType = getJdkType(rvmType);
+        Class<?> jdkType = getJdkType(rvmType);
         if (jdkType == null)
           continue;  // won't need the field info
 
@@ -1180,7 +1402,7 @@
           bootImageTypeFields.put(key, fieldInfo);
           // Now do all the superclasses if they don't already exist
           // Can't add them in next loop as Iterator's don't allow updates to collection
-          for (Class cls = jdkType.getSuperclass(); cls != null; cls = cls.getSuperclass()) {
+          for (Class<?> cls = jdkType.getSuperclass(); cls != null; cls = cls.getSuperclass()) {
             key = new Key(cls);
             fieldInfo = bootImageTypeFields.get(key);
             if (fieldInfo != null) {
@@ -1200,7 +1422,7 @@
           if (verbose >= 1) say("bootImageTypeField entry has no rvmType:"+fieldInfo.jdkType);
           continue;
         }
-        Class jdkType   = fieldInfo.jdkType;
+        Class<?> jdkType   = fieldInfo.jdkType;
         if (verbose >= 1) say("building static and instance fieldinfo for " + rvmType);
 
         // First the static fields
@@ -1289,7 +1511,7 @@
         if (!rvmType.isClassType())
           continue; // arrays and primitives have no static fields
 
-        Class jdkType = getJdkType(rvmType);
+        Class<?> jdkType = getJdkType(rvmType);
         if (jdkType == null && verbose >= 1) {
           say("host has no class \"" + rvmType + "\"");
         }
@@ -1481,6 +1703,101 @@
   }
 
   /**
+   * Write a field that contains a reference to the boot image
+   * @param fieldLocation address in boot image of field
+   * @param referencedObject the object whose address will be written at this
+   * location
+   * @param parentObject object containing this fieldLocation
+   * @param objField true if this word is an object field (as opposed
+   * to a static, or tib, or some other metadata)
+   * @param root Does this slot contain a possible reference into the heap?
+   * (objField must also be true)
+   * @param rvmFieldName Name of the field
+   * @param rvmFieldType Type of the field
+    */
+  private static void copyReferenceFieldToBootImage(Address fieldLocation, Object referencedObject,
+      Object parentObject, boolean objField, boolean root, String rvmFieldName,
+      VM_TypeReference rvmFieldType) throws IllegalAccessException {
+    if (referencedObject == null) {
+      bootImage.setNullAddressWord(fieldLocation, objField, root, true);
+    } else {
+      BootImageMap.Entry mapEntry = BootImageMap.findOrCreateEntry(referencedObject);
+      if (mapEntry.imageAddress.EQ(OBJECT_NOT_PRESENT)) {
+        if (rvmFieldName == null || !copyKnownClasspathInstanceField(parentObject, rvmFieldName, rvmFieldType, fieldLocation)) {
+          // object not part of bootimage: install null reference
+          if (verbose >= 2) traceContext.traceObjectNotInBootImage();
+          bootImage.setNullAddressWord(fieldLocation, objField, root, false);
+        }
+      } else if (mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
+        Address imageAddress;
+        if (true) {
+          // Normal collection based traversal
+          mapEntry.addLinkingAddress(fieldLocation, objField, root, rvmFieldName, rvmFieldType, parentObject);
+          if (!pendingEntries.contains(mapEntry)) {
+            pendingEntries.add(mapEntry);
+          }
+          imageAddress = OBJECT_ALLOCATION_DEFERRED;
+          root = false;
+        } else {
+          // Recurse placing work on the stack
+          mapEntry.imageAddress = copyToBootImage(referencedObject, false, Address.max(), parentObject, false);
+          imageAddress = mapEntry.imageAddress;
+        }
+        if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
+          if (verbose >= 2) traceContext.traceObjectNotInBootImage();
+          if (!copyKnownClasspathInstanceField(parentObject, rvmFieldName, rvmFieldType, fieldLocation)) {
+            // object not part of bootimage: install null reference
+            if (verbose >= 2) traceContext.traceObjectNotInBootImage();
+            bootImage.setNullAddressWord(fieldLocation, objField, root, false);
+          }
+        } else {
+          bootImage.setAddressWord(fieldLocation, imageAddress.toWord(), objField, root);
+        }
+      } else {
+        bootImage.setAddressWord(fieldLocation, mapEntry.imageAddress.toWord(), objField, root);
+      }
+    }
+  }
+
+  /**
+   * Process any entries that have been deferred
+   * @throws IllegalAccessException
+   */
+  private static void processPendingEntries() throws IllegalAccessException {
+    while (!pendingEntries.isEmpty()) {
+      BootImageMap.Entry mapEntry = pendingEntries.remove();
+      if (mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
+        mapEntry.imageAddress = copyToBootImage(mapEntry.jdkObject, false, Address.max(), null, false);
+      }
+      fixupLinkAddresses(mapEntry);
+    }
+  }
+
+  /**
+   * Iterate over link address registered with entry writing out boot image address
+   * @param mapEntry entry containing addresses to fix up
+   * @return number of entries fixed up
+   * @throws IllegalAccessException
+   */
+  private static int fixupLinkAddresses(BootImageMap.Entry mapEntry) throws IllegalAccessException {
+    int count = 0;
+    BootImageMap.Entry.LinkInfo info = mapEntry.removeLinkingAddress();
+    while(info != null) {
+      if (mapEntry.imageAddress.EQ(OBJECT_NOT_PRESENT)) {
+        if (info.rvmFieldName == null || !copyKnownClasspathInstanceField(info.parent, info.rvmFieldName, info.rvmFieldType, info.addressToFixup)) {
+          // object not part of bootimage: install null reference
+          if (verbose >= 2) traceContext.traceObjectNotInBootImage();
+          bootImage.setNullAddressWord(info.addressToFixup, info.objField, info.root, false);
+        }
+      } else {
+        bootImage.setAddressWord(info.addressToFixup, mapEntry.imageAddress.toWord(), info.objField, info.root);
+      }
+      info = mapEntry.removeLinkingAddress();
+      count++;
+    }
+    return count;
+  }
+  /**
    * Copy an object (and, recursively, any of its fields or elements that
    * are references) from host jdk address space into image.
    *
@@ -1497,19 +1814,15 @@
       Address overwriteAddress, Object parentObject, boolean untraced) throws IllegalAccessException
   {
     try {
-      //
       // Return object if it is already copied and not being overwritten
-      //
       BootImageMap.Entry mapEntry = BootImageMap.findOrCreateEntry(jdkObject);
-      if ((!mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) && overwriteAddress.isMax())
+      if ((!mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) && overwriteAddress.isMax()) {
         return mapEntry.imageAddress;
+      }
 
       if (verbose >= 2) depth++;
-
-      //
       // fetch object's type information
-      //
-      Class   jdkType = jdkObject.getClass();
+      Class<?>   jdkType = jdkObject.getClass();
       VM_Type rvmType = getRvmType(jdkType);
       if (rvmType == null) {
         if (verbose >= 2) traverseObject(jdkObject);
@@ -1517,369 +1830,342 @@
         return OBJECT_NOT_PRESENT; // object not part of bootimage
       }
 
-      //
       // copy object to image
-      //
       if (jdkType.isArray()) {
+        // allocate space in image prior to recursing
+        int arrayCount       = Array.getLength(jdkObject);
         VM_Array rvmArrayType = rvmType.asArray();
-
-        //
-        // allocate space in image
-        //
-        int arrayCount       = Array.getLength(jdkObject);
         Address arrayImageAddress = (overwriteAddress.isMax()) ? bootImage.allocateArray(rvmArrayType, arrayCount) : overwriteAddress;
         mapEntry.imageAddress = arrayImageAddress;
+        mapEntry.imageAddress = copyArrayToBootImage(arrayCount, arrayImageAddress, jdkObject, jdkType,
+            rvmArrayType, allocOnly, overwriteAddress, parentObject, untraced);
+        // copy object's type information block into image, if it's not there
+        // already
+        if (!allocOnly) {
 
-        if (verbose >= 2) {
-          if (depth == depthCutoff)
-            say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
-          else if (depth < depthCutoff) {
-            String tab = SPACES.substring(0,depth+1);
-            if (depth == 0 && jtocCount >= 0)
-              tab = tab + "jtoc #" + String.valueOf(jtocCount) + ": ";
-            int arraySize = rvmArrayType.getInstanceSize(arrayCount);
-            say(tab, "Copying array  ", jdkType.getName(),
-                "   length=", String.valueOf(arrayCount),
-                (arraySize >= LARGE_ARRAY_SIZE) ? " large object!!!" : "");
+          if (verbose >= 2) traceContext.push("", jdkObject.getClass().getName(), "tib");
+          Address tibImageAddress = copyToBootImage(rvmType.getTypeInformationBlock(), allocOnly, Address.max(), jdkObject, false);
+          if (verbose >= 2) traceContext.pop();
+          if (tibImageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
+            fail("can't copy tib for " + jdkObject);
           }
+          VM_ObjectModel.setTIB(bootImage, mapEntry.imageAddress, tibImageAddress, rvmType);
         }
+      } else if (rvmType == VM_Type.ObjectReferenceArrayType || rvmType.getTypeRef().isRuntimeTable()) {
+        Object backing = getBackingArray(rvmType, jdkObject);
 
-        VM_Type rvmElementType = rvmArrayType.getElementType();
+        /* Copy the backing array, and then replace its TIB */
+        mapEntry.imageAddress = copyToBootImage(backing, allocOnly, overwriteAddress, jdkObject, rvmType.getTypeRef().isRuntimeTable());
 
-        // Show info on reachability of int arrays
-        //
-        if (false && rvmElementType.equals(VM_Type.IntType)) {
-          if (parentObject != null) {
-            Class parentObjectType = parentObject.getClass();
-            VM.sysWrite("Copying int array (", 4 * ((int []) jdkObject).length);
-            VM.sysWriteln(" bytes) from parent object of type ", parentObjectType.toString());
-          } else {
-            VM.sysWriteln("Copying int array from no parent object");
+        if (!allocOnly) {
+          if (verbose >= 2) {
+            depth--;
+            traceContext.push("", jdkObject.getClass().getName(), "tib");
           }
-        }
-
-        //
-        // copy array elements from host jdk address space into image
-        // recurse on values that are references
-        //
-        if (rvmElementType.isPrimitiveType()) {
-          // array element is logical or numeric type
-          if (rvmElementType.equals(VM_Type.BooleanType)) {
-            boolean[] values = (boolean[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setByte(arrayImageAddress.plus(i), values[i] ? 1 : 0);
-          } else if (rvmElementType.equals(VM_Type.ByteType)) {
-            byte[] values = (byte[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setByte(arrayImageAddress.plus(i), values[i]);
-          } else if (rvmElementType.equals(VM_Type.CharType)) {
-            char[] values = (char[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setHalfWord(arrayImageAddress.plus(i << LOG_BYTES_IN_CHAR), values[i]);
-          } else if (rvmElementType.equals(VM_Type.ShortType)) {
-            short[] values = (short[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setHalfWord(arrayImageAddress.plus(i << LOG_BYTES_IN_SHORT), values[i]);
-          } else if (rvmElementType.equals(VM_Type.IntType)) {
-            int[] values = (int[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setFullWord(arrayImageAddress.plus(i << LOG_BYTES_IN_INT), values[i]);
-          } else if (rvmElementType.equals(VM_Type.LongType)) {
-            long[] values = (long[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setDoubleWord(arrayImageAddress.plus(i << LOG_BYTES_IN_LONG), values[i]);
-          } else if (rvmElementType.equals(VM_Type.FloatType)) {
-            float[] values = (float[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setFullWord(arrayImageAddress.plus(i << LOG_BYTES_IN_FLOAT),
-                  Float.floatToIntBits(values[i]));
-          } else if (rvmElementType.equals(VM_Type.DoubleType)) {
-            double[] values = (double[]) jdkObject;
-            for (int i = 0; i < arrayCount; ++i)
-              bootImage.setDoubleWord(arrayImageAddress.plus(i << LOG_BYTES_IN_DOUBLE),
-                  Double.doubleToLongBits(values[i]));
-          } else {
-            fail("unexpected primitive array type: " + rvmArrayType);
+          Address tibImageAddress = copyToBootImage(rvmType.getTypeInformationBlock(), allocOnly, Address.max(), jdkObject, false);
+          if (verbose >= 2) {
+            traceContext.pop();
+            depth++;
           }
-        } else {
-          // array element is reference type
-          boolean isTIB = parentObject instanceof VM_TIB;
-          Object[] values = (Object []) jdkObject;
-          Class jdkClass = jdkObject.getClass();
-          if (!allocOnly) {
-            for (int i = 0; i<arrayCount; ++i) {
-              if (values[i] != null) {
-                if (verbose >= 2) traceContext.push(values[i].getClass().getName(), jdkClass.getName(), i);
-                if (isTIB && values[i] instanceof Word) {
-                  bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), (Word)values[i], false, false);
-                } else if (isTIB && values[i] == VM_LazyCompilationTrampoline.instructions) {
-                  Address codeAddress = arrayImageAddress.plus(((VM_TIB)parentObject).lazyMethodInvokerTrampolineIndex() << LOG_BYTES_IN_ADDRESS);
-                  bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), codeAddress.toWord(), false, false);
-                } else {
-                  Address imageAddress = copyToBootImage(values[i], allocOnly, Address.max(), jdkObject, false);
-                  if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
-                    // object not part of bootimage: install null reference
-                    if (verbose >= 2) traceContext.traceObjectNotInBootImage();
-                    bootImage.setNullAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), !untraced, !untraced, false);
-                  } else {
-                    bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), imageAddress.toWord(), !untraced, !untraced);
-                  }
-                }
-                if (verbose >= 2) traceContext.pop();
-              } else {
-                bootImage.setNullAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), !untraced, !untraced, true);
-              }
-            }
+          if (tibImageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
+            fail("can't copy tib for " + jdkObject);
           }
+          VM_ObjectModel.setTIB(bootImage, mapEntry.imageAddress, tibImageAddress, rvmType);
         }
+      } else if (rvmType.getTypeRef().isUnboxedArrayType() ||
+          rvmType.getTypeRef().isRuntimeTable() ||
+          rvmType == VM_Type.AddressArrayType ||
+          rvmType == VM_Type.CodeArrayType ||
+          rvmType == VM_Type.ExtentArrayType ||
+          rvmType == VM_Type.OffsetArrayType ||
+          rvmType == VM_Type.ObjectReferenceArrayType ||
+          rvmType == VM_Type.WordArrayType) {
+        Object backing = getBackingArray(rvmType, jdkObject);
+        mapEntry.imageAddress = copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
+      } else if (rvmType.getTypeRef().isMagicType()) {
+        VM.sysWriteln("Unhandled copying of magic type: " + rvmType.getDescriptor().toString() +
+            " in object of type " + parentObject.getClass().toString());
+        VM.sysFail("incomplete boot image support");
       } else {
-        if (rvmType == VM_Type.ObjectReferenceArrayType || rvmType.getTypeRef().isRuntimeTable()) {
-          if (verbose >= 2) depth--;
-          Object backing;
-          if (rvmType == VM_Type.ObjectReferenceArrayType) {
-            backing = ((ObjectReferenceArray)jdkObject).getBacking();
-          } else if (rvmType == VM_Type.TIBType) {
-            backing = ((VM_TIB)jdkObject).getBacking();
-          } else if (rvmType == VM_Type.IMTType) {
-            backing = ((VM_IMT)jdkObject).getBacking();
-          } else if (rvmType == VM_Type.ITableType) {
-            backing = ((VM_ITable)jdkObject).getBacking();
-          } else if (rvmType == VM_Type.ITableArrayType) {
-            backing = ((VM_ITableArray)jdkObject).getBacking();
-          } else if (rvmType == VM_Type.ProcessorTableType) {
-            backing = ((VM_ProcessorTable)jdkObject).getBacking();
-          } else if (rvmType == VM_Type.FunctionTableType) {
-            backing = ((VM_FunctionTable)jdkObject).getBacking();
-          } else {
-            fail("unexpected runtime table type: " + rvmType);
-            backing = null;
-          }
+        // allocate space in image
+        VM_Class rvmScalarType = rvmType.asClass();
+        Address scalarImageAddress = (overwriteAddress.isMax()) ? bootImage.allocateScalar(rvmScalarType) : overwriteAddress;
+        mapEntry.imageAddress = scalarImageAddress;
+        mapEntry.imageAddress = copyClassToBootImage(scalarImageAddress, jdkObject, jdkType, rvmScalarType,
+            allocOnly, overwriteAddress, parentObject, untraced);
+        // copy object's type information block into image, if it's not there
+        // already
+        if (!allocOnly) {
 
-          /* Copy the backing array, and then replace its TIB */
-          mapEntry.imageAddress = copyToBootImage(backing, allocOnly, overwriteAddress, jdkObject, rvmType.getTypeRef().isRuntimeTable());
-
-          if (!allocOnly) {
-            if (verbose >= 2) traceContext.push("", jdkObject.getClass().getName(), "tib");
-            Address tibImageAddress = copyToBootImage(rvmType.getTypeInformationBlock(), allocOnly, Address.max(), jdkObject, false);
-            if (verbose >= 2) traceContext.pop();
-            if (tibImageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
-              fail("can't copy tib for " + jdkObject);
-            }
-            VM_ObjectModel.setTIB(bootImage, mapEntry.imageAddress, tibImageAddress, rvmType);
+          if (verbose >= 2) traceContext.push("", jdkObject.getClass().getName(), "tib");
+          Address tibImageAddress = copyToBootImage(rvmType.getTypeInformationBlock(), allocOnly, Address.max(), jdkObject, false);
+          if (verbose >= 2) traceContext.pop();
+          if (tibImageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
+            fail("can't copy tib for " + jdkObject);
           }
-
-          return mapEntry.imageAddress;
+          VM_ObjectModel.setTIB(bootImage, mapEntry.imageAddress, tibImageAddress, rvmType);
         }
+      }
 
-        if (rvmType == VM_Type.AddressArrayType) {
-          if (verbose >= 2) depth--;
-          AddressArray addrArray = (AddressArray) jdkObject;
-          Object backing = addrArray.getBacking();
-          return copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
-        }
+      if (verbose >= 2) depth--;
+      return mapEntry.imageAddress;
 
-        if (rvmType == VM_Type.OffsetArrayType) {
-          if (verbose >= 2) depth--;
-          OffsetArray addrArray = (OffsetArray) jdkObject;
-          Object backing = addrArray.getBacking();
-          return copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
-        }
+    } catch (Error e) {
+      e = new Error(e.getMessage()+ "\nwhile copying " +
+          jdkObject + (jdkObject != null ? ":"+jdkObject.getClass():"") + " from " +
+          parentObject + (parentObject != null ? ":"+parentObject.getClass():""),
+          e.getCause() != null? e.getCause() : e);
+      throw e;
+    }
+  }
 
-        if (rvmType == VM_Type.WordArrayType) {
-          if (verbose >= 2) depth--;
-          WordArray addrArray = (WordArray) jdkObject;
-          Object backing = addrArray.getBacking();
-          return copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
-        }
+  /**
+   * Write an object instantiating a class to the boot image
+   * @param scalarImageAddress address already allocated for object
+   * @param jdkObject object to write
+   * @param jdkType java.lang.Class of object
+   * @param rvmScalarType RVM class loader version of type
+   * @param allocOnly allocate the object only?
+   * @param overwriteAddress
+   * @param parentObject
+   * @param untraced
+   * @return
+   * @throws IllegalAccessException
+   */
+  private static Address copyClassToBootImage(Address scalarImageAddress, Object jdkObject, Class<?> jdkType,
+      VM_Class rvmScalarType, boolean allocOnly, Address overwriteAddress, Object parentObject, boolean  untraced)
+  throws IllegalAccessException {
+    if (verbose >= 2) {
+      if (depth == depthCutoff)
+        say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
+      else if (depth < depthCutoff) {
+        String tab = SPACES.substring(0,depth+1);
+        if (depth == 0 && jtocCount >= 0)
+          tab = tab + "jtoc #" + String.valueOf(jtocCount) + " ";
+        int scalarSize = rvmScalarType.getInstanceSize();
+        say(tab, "Copying object ", jdkType.getName(),
+            "   size=", String.valueOf(scalarSize),
+            (scalarSize >= LARGE_SCALAR_SIZE) ? " large object!!!" : "");
+      }
+    }
 
-        if (rvmType == VM_Type.ExtentArrayType) {
-          if (verbose >= 2) depth--;
-          ExtentArray addrArray = (ExtentArray) jdkObject;
-          Object backing = addrArray.getBacking();
-          return copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
-        }
+    // copy object fields from host jdk address space into image
+    // recurse on values that are references
+    VM_Field[] rvmFields = rvmScalarType.getInstanceFields();
+    for (int i = 0; i < rvmFields.length; ++i) {
+      VM_Field rvmField       = rvmFields[i];
+      VM_TypeReference rvmFieldType   = rvmField.getType();
+      Address rvmFieldAddress = scalarImageAddress.plus(rvmField.getOffset());
+      String  rvmFieldName    = rvmField.getName().toString();
+      Field   jdkFieldAcc     = getJdkFieldAccessor(jdkType, i, INSTANCE_FIELD);
 
-        if (rvmType == VM_Type.CodeArrayType) {
-          if (verbose >= 2) depth--;
-          VM_CodeArray codeArray = (VM_CodeArray) jdkObject;
-          Object backing = codeArray.getBacking();
-          return copyMagicArrayToBootImage(backing, rvmType.asArray(), allocOnly, overwriteAddress, parentObject);
-        }
+      boolean untracedField = rvmField.isUntraced() || untraced;
 
-        if (rvmType.getTypeRef().isMagicType()) {
-          VM.sysWriteln("Unhandled copying of magic type: " + rvmType.getDescriptor().toString() +
-              " in object of type " + parentObject.getClass().toString());
-          VM.sysFail("incomplete boot image support");
+      if (jdkFieldAcc == null) {
+        // Field not found via reflection
+        if (!copyKnownClasspathInstanceField(jdkObject, rvmFieldName, rvmFieldType, rvmFieldAddress)) {
+          // Field wasn't a known Classpath field so write null
+          if (verbose >= 2) traceContext.push(rvmFieldType.toString(),
+              jdkType.getName(), rvmFieldName);
+          if (verbose >= 2) traceContext.traceFieldNotInHostJdk();
+          if (verbose >= 2) traceContext.pop();
+          if (rvmFieldType.isPrimitiveType()) {
+            switch (rvmField.getType().getMemoryBytes()) {
+            case 1: bootImage.setByte(rvmFieldAddress, 0);          break;
+            case 2: bootImage.setHalfWord(rvmFieldAddress, 0);      break;
+            case 4: bootImage.setFullWord(rvmFieldAddress, 0);      break;
+            case 8: bootImage.setDoubleWord(rvmFieldAddress, 0L);   break;
+            default:fail("unexpected field type: " + rvmFieldType); break;
+            }
+          } else {
+            bootImage.setNullAddressWord(rvmFieldAddress, !untracedField, !untracedField, false);
+          }
         }
+        continue;
+      }
 
-        //
-        // allocate space in image
-        //
-        VM_Class rvmScalarType = rvmType.asClass();
-        Address scalarImageAddress = (overwriteAddress.isMax()) ? bootImage.allocateScalar(rvmScalarType) : overwriteAddress;
-        mapEntry.imageAddress = scalarImageAddress;
-
-        if (verbose >= 2) {
-          if (depth == depthCutoff)
-            say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
-          else if (depth < depthCutoff) {
-            String tab = SPACES.substring(0,depth+1);
-            if (depth == 0 && jtocCount >= 0)
-              tab = tab + "jtoc #" + String.valueOf(jtocCount) + " ";
-            int scalarSize = rvmScalarType.getInstanceSize();
-            say(tab, "Copying object ", jdkType.getName(),
-                "   size=", String.valueOf(scalarSize),
-                (scalarSize >= LARGE_SCALAR_SIZE) ? " large object!!!" : "");
+      if (rvmFieldType.isPrimitiveType()) {
+        // field is logical or numeric type
+        if (rvmFieldType.isBooleanType()) {
+          bootImage.setByte(rvmFieldAddress,
+              jdkFieldAcc.getBoolean(jdkObject) ? 1 : 0);
+        } else if (rvmFieldType.isByteType()) {
+          bootImage.setByte(rvmFieldAddress,
+              jdkFieldAcc.getByte(jdkObject));
+        } else if (rvmFieldType.isCharType()) {
+          bootImage.setHalfWord(rvmFieldAddress,
+              jdkFieldAcc.getChar(jdkObject));
+        } else if (rvmFieldType.isShortType()) {
+          bootImage.setHalfWord(rvmFieldAddress,
+              jdkFieldAcc.getShort(jdkObject));
+        } else if (rvmFieldType.isIntType()) {
+          try {
+            bootImage.setFullWord(rvmFieldAddress, jdkFieldAcc.getInt(jdkObject));
+          } catch (IllegalArgumentException ex) {
+            System.out.println("type " + rvmScalarType + ", field " + rvmField);
+            throw ex;
           }
+        } else if (rvmFieldType.isLongType()) {
+          bootImage.setDoubleWord(rvmFieldAddress,
+              jdkFieldAcc.getLong(jdkObject));
+        } else if (rvmFieldType.isFloatType()) {
+          float f = jdkFieldAcc.getFloat(jdkObject);
+          bootImage.setFullWord(rvmFieldAddress,
+              Float.floatToIntBits(f));
+        } else if (rvmFieldType.isDoubleType()) {
+          double d = jdkFieldAcc.getDouble(jdkObject);
+          bootImage.setDoubleWord(rvmFieldAddress,
+              Double.doubleToLongBits(d));
+        } else if (rvmFieldType.equals(VM_TypeReference.Address) ||
+            rvmFieldType.equals(VM_TypeReference.Word) ||
+            rvmFieldType.equals(VM_TypeReference.Extent) ||
+            rvmFieldType.equals(VM_TypeReference.Offset)) {
+          Object o = jdkFieldAcc.get(jdkObject);
+          String msg = " instance field " + rvmField.toString();
+          boolean warn = rvmFieldType.equals(VM_TypeReference.Address);
+          bootImage.setAddressWord(rvmFieldAddress, getWordValue(o, msg, warn), false, false);
+        } else {
+          fail("unexpected primitive field type: " + rvmFieldType);
         }
+      } else {
+        // field is reference type
+        Object value = jdkFieldAcc.get(jdkObject);
+        if (!allocOnly) {
+          Class<?> jdkClass = jdkFieldAcc.getDeclaringClass();
+          if (verbose >= 2) traceContext.push(value.getClass().getName(),
+              jdkClass.getName(),
+              jdkFieldAcc.getName());
+          copyReferenceFieldToBootImage(rvmFieldAddress, value, jdkObject,
+              !untracedField, !(untracedField || rvmField.isFinal()), rvmFieldName, rvmFieldType);
+        }
+      }
+    }
+    return scalarImageAddress;
+  }
 
-        //
-        // copy object fields from host jdk address space into image
-        // recurse on values that are references
-        //
-        VM_Field[] rvmFields = rvmScalarType.getInstanceFields();
-        for (int i = 0, n = rvmFields.length; i < n; ++i) {
-          VM_Field rvmField       = rvmFields[i];
-          VM_TypeReference rvmFieldType   = rvmField.getType();
-          Address rvmFieldAddress = scalarImageAddress.plus(rvmField.getOffset());
-          String  rvmFieldName    = rvmField.getName().toString();
-          Field   jdkFieldAcc     = getJdkFieldAccessor(jdkType, i, INSTANCE_FIELD);
+  /**
+   * Write array to boot image
+   * @param arrayCount
+   * @param arrayImageAddress
+   * @param jdkObject
+   * @param jdkType
+   * @param rvmArrayType
+   * @param allocOnly
+   * @param overwriteAddress
+   * @param parentObject
+   * @param untraced
+   * @return
+   * @throws IllegalAccessException
+   */
+  private static Address copyArrayToBootImage(int arrayCount, Address arrayImageAddress, Object jdkObject, Class<?> jdkType, VM_Array rvmArrayType,
+      boolean allocOnly, Address overwriteAddress, Object parentObject, boolean  untraced)
+  throws IllegalAccessException {
+    if (verbose >= 2) {
+      if (depth == depthCutoff)
+        say(SPACES.substring(0,depth+1), "TOO DEEP: cutting off");
+      else if (depth < depthCutoff) {
+        String tab = SPACES.substring(0,depth+1);
+        if (depth == 0 && jtocCount >= 0)
+          tab = tab + "jtoc #" + String.valueOf(jtocCount) + ": ";
+        int arraySize = rvmArrayType.getInstanceSize(arrayCount);
+        say(tab, "Copying array  ", jdkType.getName(),
+            "   length=", String.valueOf(arrayCount),
+            (arraySize >= LARGE_ARRAY_SIZE) ? " large object!!!" : "");
+      }
+    }
 
-          boolean untracedField = rvmField.isUntraced() || untraced;
+    VM_Type rvmElementType = rvmArrayType.getElementType();
 
-          if (jdkFieldAcc == null) {
-            // Field not found via reflection
-            if (!copyKnownClasspathInstanceField(jdkObject, rvmFieldName, rvmFieldType, rvmFieldAddress)) {
-              // Field wasn't a known Classpath field so write null
-              if (verbose >= 2) traceContext.push(rvmFieldType.toString(),
-                  jdkType.getName(), rvmFieldName);
-              if (verbose >= 2) traceContext.traceFieldNotInHostJdk();
-              if (verbose >= 2) traceContext.pop();
-              if (rvmFieldType.isPrimitiveType()) {
-                switch (rvmField.getType().getMemoryBytes()) {
-                case 1: bootImage.setByte(rvmFieldAddress, 0);          break;
-                case 2: bootImage.setHalfWord(rvmFieldAddress, 0);      break;
-                case 4: bootImage.setFullWord(rvmFieldAddress, 0);      break;
-                case 8: bootImage.setDoubleWord(rvmFieldAddress, 0L);   break;
-                default:fail("unexpected field type: " + rvmFieldType); break;
-                }
-              } else {
-                bootImage.setNullAddressWord(rvmFieldAddress, !untracedField, !untracedField, false);
-              }
-            }
-            continue;
-          }
-
-          if (rvmFieldType.isPrimitiveType()) {
-            // field is logical or numeric type
-            if (rvmFieldType.isBooleanType()) {
-              bootImage.setByte(rvmFieldAddress,
-                  jdkFieldAcc.getBoolean(jdkObject) ? 1 : 0);
-            } else if (rvmFieldType.isByteType()) {
-              bootImage.setByte(rvmFieldAddress,
-                  jdkFieldAcc.getByte(jdkObject));
-            } else if (rvmFieldType.isCharType()) {
-              bootImage.setHalfWord(rvmFieldAddress,
-                  jdkFieldAcc.getChar(jdkObject));
-            } else if (rvmFieldType.isShortType()) {
-              bootImage.setHalfWord(rvmFieldAddress,
-                  jdkFieldAcc.getShort(jdkObject));
-            } else if (rvmFieldType.isIntType()) {
-              try {
-                bootImage.setFullWord(rvmFieldAddress,
-                    jdkFieldAcc.getInt(jdkObject));
-              } catch (IllegalArgumentException ex) {
-                System.out.println("type " + rvmScalarType + ", field " + rvmField);
-                throw ex;
-              }
-            } else if (rvmFieldType.isLongType()) {
-              bootImage.setDoubleWord(rvmFieldAddress,
-                  jdkFieldAcc.getLong(jdkObject));
-            } else if (rvmFieldType.isFloatType()) {
-              float f = jdkFieldAcc.getFloat(jdkObject);
-              bootImage.setFullWord(rvmFieldAddress,
-                  Float.floatToIntBits(f));
-            } else if (rvmFieldType.isDoubleType()) {
-              double d = jdkFieldAcc.getDouble(jdkObject);
-              bootImage.setDoubleWord(rvmFieldAddress,
-                  Double.doubleToLongBits(d));
-            } else if (rvmFieldType.equals(VM_TypeReference.Address) ||
-                rvmFieldType.equals(VM_TypeReference.Word) ||
-                rvmFieldType.equals(VM_TypeReference.Extent) ||
-                rvmFieldType.equals(VM_TypeReference.Offset)) {
-              Object o = jdkFieldAcc.get(jdkObject);
-              String msg = " instance field " + rvmField.toString();
-              boolean warn = rvmFieldType.equals(VM_TypeReference.Address);
-              bootImage.setAddressWord(rvmFieldAddress, getWordValue(o, msg, warn), false, false);
+    // copy array elements from host jdk address space into image
+    // recurse on values that are references
+    if (rvmElementType.isPrimitiveType()) {
+      // array element is logical or numeric type
+      if (rvmElementType.equals(VM_Type.BooleanType)) {
+        boolean[] values = (boolean[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setByte(arrayImageAddress.plus(i), values[i] ? 1 : 0);
+      } else if (rvmElementType.equals(VM_Type.ByteType)) {
+        byte[] values = (byte[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setByte(arrayImageAddress.plus(i), values[i]);
+      } else if (rvmElementType.equals(VM_Type.CharType)) {
+        char[] values = (char[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setHalfWord(arrayImageAddress.plus(i << LOG_BYTES_IN_CHAR), values[i]);
+      } else if (rvmElementType.equals(VM_Type.ShortType)) {
+        short[] values = (short[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setHalfWord(arrayImageAddress.plus(i << LOG_BYTES_IN_SHORT), values[i]);
+      } else if (rvmElementType.equals(VM_Type.IntType)) {
+        int[] values = (int[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setFullWord(arrayImageAddress.plus(i << LOG_BYTES_IN_INT), values[i]);
+      } else if (rvmElementType.equals(VM_Type.LongType)) {
+        long[] values = (long[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setDoubleWord(arrayImageAddress.plus(i << LOG_BYTES_IN_LONG), values[i]);
+      } else if (rvmElementType.equals(VM_Type.FloatType)) {
+        float[] values = (float[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setFullWord(arrayImageAddress.plus(i << LOG_BYTES_IN_FLOAT),
+              Float.floatToIntBits(values[i]));
+      } else if (rvmElementType.equals(VM_Type.DoubleType)) {
+        double[] values = (double[]) jdkObject;
+        for (int i = 0; i < arrayCount; ++i)
+          bootImage.setDoubleWord(arrayImageAddress.plus(i << LOG_BYTES_IN_DOUBLE),
+              Double.doubleToLongBits(values[i]));
+      } else {
+        fail("unexpected primitive array type: " + rvmArrayType);
+      }
+    } else {
+      // array element is reference type
+      boolean isTIB = parentObject instanceof VM_TIB;
+      Object[] values = (Object []) jdkObject;
+      Class<?> jdkClass = jdkObject.getClass();
+      if (!allocOnly) {
+        for (int i = 0; i<arrayCount; ++i) {
+          if (values[i] != null) {
+            if (verbose >= 2) traceContext.push(values[i].getClass().getName(), jdkClass.getName(), i);
+            if (isTIB && values[i] instanceof Word) {
+              bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), (Word)values[i], false, false);
+            } else if (isTIB && values[i] == VM_LazyCompilationTrampoline.instructions) {
+              Address codeAddress = arrayImageAddress.plus(((VM_TIB)parentObject).lazyMethodInvokerTrampolineIndex() << LOG_BYTES_IN_ADDRESS);
+              bootImage.setAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), codeAddress.toWord(), false, false);
             } else {
-              fail("unexpected primitive field type: " + rvmFieldType);
+              copyReferenceFieldToBootImage(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), values[i],
+                  jdkObject, !untraced, !untraced, null, null);
             }
+            if (verbose >= 2) traceContext.pop();
           } else {
-            // field is reference type
-            Object value = jdkFieldAcc.get(jdkObject);
-            if (!allocOnly) {
-              if (value != null) {
-                Class jdkClass = jdkFieldAcc.getDeclaringClass();
-                if (verbose >= 2) traceContext.push(value.getClass().getName(),
-                    jdkClass.getName(),
-                    jdkFieldAcc.getName());
-                Address imageAddress = copyToBootImage(value, allocOnly, Address.max(), jdkObject, false);
-                if (imageAddress.EQ(OBJECT_NOT_PRESENT)) {
-                  if (!copyKnownClasspathInstanceField(jdkObject, rvmFieldName, rvmFieldType, rvmFieldAddress)) {
-                    // object not part of bootimage: install null reference
-                    if (verbose >= 2) traceContext.traceObjectNotInBootImage();
-                    bootImage.setNullAddressWord(rvmFieldAddress, !untracedField, !untracedField, false);
-                  }
-                } else
-                  bootImage.setAddressWord(rvmFieldAddress, imageAddress.toWord(), !untracedField, !(untracedField || rvmField.isFinal()));
-                if (verbose >= 2) traceContext.pop();
-              } else {
-                bootImage.setNullAddressWord(rvmFieldAddress, !untracedField, !untracedField, true);
-              }
-            }
+            bootImage.setNullAddressWord(arrayImageAddress.plus(i << LOG_BYTES_IN_ADDRESS), !untraced, !untraced, true);
           }
         }
       }
-
-      //
-      // copy object's type information block into image, if it's not there
-      // already
-      //
-      if (!allocOnly) {
-
-        if (verbose >= 2) traceContext.push("", jdkObject.getClass().getName(), "tib");
-        Address tibImageAddress = copyToBootImage(rvmType.getTypeInformationBlock(), allocOnly, Address.max(), jdkObject, false);
-        if (verbose >= 2) traceContext.pop();
-        if (tibImageAddress.EQ(OBJECT_NOT_ALLOCATED)) {
-          fail("can't copy tib for " + jdkObject);
-        }
-        VM_ObjectModel.setTIB(bootImage, mapEntry.imageAddress, tibImageAddress, rvmType);
-      }
-
-      if (verbose >= 2) depth--;
-
-      return mapEntry.imageAddress;
-    } catch (Error e) {
-      e = new Error(e.getMessage()+ "\nwhile copying " +
-          jdkObject + (jdkObject != null ? ":"+jdkObject.getClass():"") + " from " +
-          parentObject + (parentObject != null ? ":"+parentObject.getClass():""),
-          e.getCause() != null? e.getCause() : e);
-      throw e;
     }
+    return arrayImageAddress;
   }
 
+  /**
+   * Copy a unboxed array type to the boot image
+   * @param jdkObject object representation
+   * @param rvmArrayType type of array
+   * @param allocOnly allocate object don't write to fields
+   * @param overwriteAddress addresss to write to if overwriting
+   * @param parentObject object containing array
+   * @return address of array
+   * @throws IllegalAccessException
+   */
   private static Address copyMagicArrayToBootImage(Object jdkObject,
                                                    VM_Array rvmArrayType,
                                                    boolean allocOnly,
                                                    Address overwriteAddress,
                                                    Object parentObject)
     throws IllegalAccessException {
-    //
     // Return object if it is already copied and not being overwritten
-    //
     BootImageMap.Entry mapEntry = BootImageMap.findOrCreateEntry(jdkObject);
-    if ((!mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) && overwriteAddress.isMax())
+    if ((!mapEntry.imageAddress.EQ(OBJECT_NOT_ALLOCATED)) && overwriteAddress.isMax()) {
       return mapEntry.imageAddress;
+    }
 
     if (verbose >= 2) depth++;
 
@@ -1982,7 +2268,7 @@
    * @param rvmFieldName the name of the field
    * @param rvmFieldType the type reference of the field
    */
-  private static boolean copyKnownClasspathStaticField(Class jdkType, String rvmFieldName,
+  private static boolean copyKnownClasspathStaticField(Class<?> jdkType, String rvmFieldName,
                                                        VM_TypeReference rvmFieldType,
                                                        Offset rvmFieldOffset) {
     if (jdkType.equals(java.lang.Number.class)) {
@@ -2128,7 +2414,7 @@
    * @param jdkObject the object containing the field
    * @param rvmFieldName the name of the field
    * @param rvmFieldType the type reference of the field
-   * @param rvmFieldAddress
+   * @param rvmFieldAddress the address that the field is being written to
    */
   private static boolean copyKnownClasspathInstanceField(Object jdkObject, String rvmFieldName, VM_TypeReference rvmFieldType, Address rvmFieldAddress)
     throws IllegalAccessException {
@@ -2165,9 +2451,9 @@
         } else if (jdkObject == java.lang.Void.TYPE) {
           value = VM_Type.VoidType;
         } else {
-          value = VM_TypeReference.findOrCreate((Class)jdkObject).peekType();
+          value = VM_TypeReference.findOrCreate((Class<?>)jdkObject).peekType();
           if (value == null) {
-             throw new Error("Failed to populate Class.type for " + jdkObject);
+             fail("Failed to populate Class.type for " + jdkObject);
           }
         }
         fieldName = "type";
@@ -2197,7 +2483,7 @@
         return false;
       }
     } else if (jdkObject instanceof java.lang.reflect.Constructor)   {
-      Constructor cons = (Constructor)jdkObject;
+      Constructor<?> cons = (Constructor<?>)jdkObject;
       if(rvmFieldName.equals("cons")) {
         // fill in this VM_Method field
         String typeName = "L" + cons.getDeclaringClass().getName().replace('.','/') + ";";
@@ -2206,7 +2492,7 @@
           throw new Error("Failed to find type for Constructor.constructor: " + cons + " " + typeName);
         }
         final VM_Class klass = type.asClass();
-        Class[] consParams = cons.getParameterTypes();
+        Class<?>[] consParams = cons.getParameterTypes();
         VM_Method constructor = null;
         loop_over_all_constructors:
         for (VM_Method vmCons : klass.getConstructorMethods()) {
@@ -2338,7 +2624,7 @@
       //
       // fetch object's type information
       //
-      Class jdkType = jdkObject.getClass();
+      Class<?> jdkType = jdkObject.getClass();
       int size = OBJECT_HEADER_SIZE;
 
       //
@@ -2351,7 +2637,7 @@
         // traverse array elements
         // recurse on values that are references
         //
-        Class jdkElementType = jdkType.getComponentType();
+        Class<?> jdkElementType = jdkType.getComponentType();
         if (jdkElementType.isPrimitive()) {
           // array element is logical or numeric type
           if (jdkElementType == Boolean.TYPE) {
@@ -2403,12 +2689,12 @@
         // traverse object fields
         // recurse on values that are references
         //
-        for (Class type = jdkType; type != null; type = type.getSuperclass()) {
+        for (Class<?> type = jdkType; type != null; type = type.getSuperclass()) {
           Field[] jdkFields = type.getDeclaredFields();
           for (int i = 0, n = jdkFields.length; i < n; ++i) {
             Field  jdkField       = jdkFields[i];
             jdkField.setAccessible(true);
-            Class  jdkFieldType   = jdkField.getType();
+            Class<?>  jdkFieldType   = jdkField.getType();
 
             if (jdkFieldType.isPrimitive()) {
               // field is logical or numeric type
@@ -2495,7 +2781,7 @@
    * @return rvm type (null --> type does not appear in list of classes
    *         comprising bootimage)
    */
-  private static VM_Type getRvmType(Class jdkType) {
+  private static VM_Type getRvmType(Class<?> jdkType) {
     return bootImageTypes.get(jdkType.getName());
   }
 
@@ -2505,7 +2791,7 @@
    * @param rvmType rvm type
    * @return jdk type (null --> type does not exist in host namespace)
    */
-  private static Class getJdkType(VM_Type rvmType) {
+  private static Class<?> getJdkType(VM_Type rvmType) {
     Throwable x;
     try {
       return Class.forName(rvmType.toString());
@@ -2537,7 +2823,7 @@
    * @param isStatic is field from Static field table, indicates which table to consult
    * @return field accessor (null --> host class does not have specified field)
    */
-  private static Field getJdkFieldAccessor(Class jdkType, int index, boolean isStatic) {
+  private static Field getJdkFieldAccessor(Class<?> jdkType, int index, boolean isStatic) {
     FieldInfo fInfo = bootImageTypeFields.get(new Key(jdkType));
     Field     f;
     if (isStatic == STATIC_FIELD) {
@@ -2800,7 +3086,7 @@
     }
 
     // Extra information on the layout of objects in the boot image
-    if (false) {
+    if (true) {
       out.println();
       out.println("Object Map");
       out.println("----------");
@@ -2833,21 +3119,6 @@
   }
 
   /**
-   * Read an integer value from the JTOC
-   * @param jtocOff offset in JTOC
-   * @return integer at offset
-   */
-  private static int getIVal(Offset jtocOff) {
-    int ival;
-    if (VM.BuildFor32Addr) {
-      ival = VM_Statics.getSlotContentsAsInt(jtocOff);
-    } else {
-      ival = (int)VM_Statics.getSlotContentsAsLong(jtocOff); // just a cookie
-    }
-    return ival;
-  }
-
-  /**
    * Read a reference from the JTOC
    * @param jtocOff offset in JTOC
    * @param fatalIfNotFound whether to terminate on failure
@@ -2905,4 +3176,93 @@
     }
     return backing;
   }
+
+  /**
+   * Get the size of the object in the boot image
+   * @param type of object
+   * @param obj we want the size of
+   * @return size of object
+   */
+  private static int getSize(VM_Type type, Object obj) {
+    if (type.isArrayType()) {
+      if (type.getTypeRef().isUnboxedArrayType() ||
+          type.getTypeRef().isRuntimeTable() ||
+          obj instanceof AddressArray ||
+          obj instanceof VM_CodeArray ||
+          obj instanceof ExtentArray ||
+          obj instanceof ObjectReferenceArray ||
+          obj instanceof OffsetArray ||
+          obj instanceof WordArray) {
+        obj = getBackingArray(type, obj);
+      }
+      if (!obj.getClass().isArray()) {
+        fail("This should be an array " + obj.getClass() + " " + type);
+      }
+      return type.asArray().getInstanceSize(Array.getLength(obj));
+    } else {
+      return type.asClass().getInstanceSize();
+    }
+  }
+
+  private static final HashMap<VM_Type, Integer> typeSizes = new HashMap<VM_Type, Integer>();
+
+  /**
+   * Get the number of non-final references of the object in the boot image
+   * @param type of object
+   * @param obj we want the size of
+   * @return number of non-final references
+   */
+  private static int getNumberOfNonFinalReferences(VM_Type type, Object obj) {
+    if (type.isArrayType()) {
+      if (type.asArray().getElementType().isReferenceType()) {
+        if (obj instanceof ObjectReferenceArray) {
+          obj = getBackingArray(type, obj);
+        }
+        if (!obj.getClass().isArray()) {
+          fail("This should be an array " + obj.getClass() + " " + type);
+        }
+        return Array.getLength(obj);
+      } else {
+        return 0;
+      }
+    } else {
+      Integer size = typeSizes.get(type);
+      if (size == null) {
+        // discount final references that aren't part of the boot image
+        size = type.asClass().getNumberOfNonFinalReferences();
+        typeSizes.put(type, size);
+      }
+      return size;
+    }
+  }
+
+  /**
+   * Get the number of non-final references of the object in the boot image
+   * @param type of object
+   * @param obj we want the size of
+   * @return number of non-final references
+   */
+  private static int getNumberOfReferences(VM_Type type, Object obj) {
+    if (type.isArrayType()) {
+      if (type.asArray().getElementType().isReferenceType()) {
+        if (obj instanceof ObjectReferenceArray) {
+          obj = getBackingArray(type, obj);
+        }
+        if (!obj.getClass().isArray()) {
+          fail("This should be an array " + obj.getClass() + " " + type);
+        }
+        return Array.getLength(obj);
+      } else {
+        return 0;
+      }
+    } else {
+      Integer size = typeSizes.get(type);
+      if (size == null) {
+        // discount final references that aren't part of the boot image
+        size = type.asClass().getReferenceOffsets().length;
+        typeSizes.put(type, size);
+      }
+      return size;
+    }
+  }
 }
Index: tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageMap.java
===================================================================
--- tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageMap.java	(revision 14341)
+++ tools/bootImageWriter/src/org/jikesrvm/tools/bootImageWriter/BootImageMap.java	(working copy)
@@ -15,7 +15,10 @@
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.Queue;
 import org.vmmagic.unboxed.Address;
+import org.jikesrvm.classloader.VM_TypeReference;
 
 /**
  * Correlate objects in host jdk with corresponding objects in target rvm
@@ -31,7 +34,7 @@
   /**
    * objectId->Entry map
    */
-  private static final ArrayList<Entry> objectIdToEntry;
+  static final ArrayList<Entry> objectIdToEntry;
 
   /**
    * Entry used to represent null object
@@ -58,7 +61,8 @@
     objectIdToEntry =  new ArrayList<Entry>(5000);
     idGenerator = 0;
     // predefine "null" object
-    nullEntry = new Entry(newId(), null, Address.zero());
+    nullEntry = new Entry(newId(), null);
+    nullEntry.imageAddress = Address.zero();
     // slot 0 reserved for "null" object entry
     objectIdToEntry.add(nullEntry);
   }
@@ -125,17 +129,58 @@
      */
     Address imageAddress;
 
+    public static class LinkInfo {
+      final Address addressToFixup;
+      final boolean objField;
+      final boolean root;
+      final String rvmFieldName;
+      final VM_TypeReference rvmFieldType;
+      final Object parent;
+      LinkInfo(Address a, boolean o, boolean r, String rvmFieldName, VM_TypeReference rvmFieldType, Object parent) {
+        addressToFixup = a;
+        objField = o;
+        root = r;
+        this.rvmFieldName = rvmFieldName;
+        this.rvmFieldType = rvmFieldType;
+        this.parent = parent;
+      }
+    }
+
     /**
+     * A list of addresses where when this value is not OBJECT_NOT_ALLOCATED
+     */
+    private Queue<LinkInfo> linkingAddresses;
+
+    /**
      * Constructor.
      * @param objectId unique id
      * @param jdkObject the JDK object
      * @param imageAddress the address of the object in the bootimage
      */
-    public Entry(Address objectId, Object jdkObject, Address imageAddress) {
+    public Entry(Address objectId, Object jdkObject) {
       this.objectId     = objectId;
       this.jdkObject    = jdkObject;
-      this.imageAddress = imageAddress;
+      this.imageAddress = OBJECT_NOT_ALLOCATED;
     }
+
+    synchronized void addLinkingAddress(Address toBeLinked, boolean objField, boolean root, String rvmFieldName, VM_TypeReference rvmFieldType, Object parent) {
+      if (linkingAddresses == null) {
+        linkingAddresses = new LinkedList<LinkInfo>();
+      }
+      linkingAddresses.add(new LinkInfo(toBeLinked, objField, root, rvmFieldName, rvmFieldType, parent));
+    }
+
+    synchronized LinkInfo removeLinkingAddress() {
+      if (linkingAddresses == null) {
+        return null;
+      } else {
+        if (linkingAddresses.peek() != null) {
+          return linkingAddresses.remove();
+        } else {
+          return null;
+        }
+      }
+    }
   }
 
   /**
@@ -154,7 +199,7 @@
       Key key   = new Key(jdkObject);
       Entry entry = keyToEntry.get(key);
       if (entry == null) {
-        entry = new Entry(newId(), jdkObject, OBJECT_NOT_ALLOCATED);
+        entry = new Entry(newId(), jdkObject);
         keyToEntry.put(key, entry);
         objectIdToEntry.add(entry);
       }
