Change Class layout to allocate an array of StaticField objects
immediately after the ClassObject, rather than use calloc.

This has the rather surprising and pleasing effect of increasing
charing about 150K per zygote-launced application, as measured at
start-up, after waiting and no-touching.

Change-Id: I6a6c9079f946eb99111326ed45f13ecfe544e4bb
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
index ff0efd3..c43b1c6 100644
--- a/vm/CheckJni.c
+++ b/vm/CheckJni.c
@@ -804,7 +804,7 @@
 static void checkStaticFieldID(JNIEnv* env, jclass jclazz, jfieldID fieldID)
 {
     ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
-    StaticField* base = clazz->sfields;
+    StaticField* base = &clazz->sfields[0];
     int fieldCount = clazz->sfieldCount;
 
     if ((StaticField*) fieldID < base ||
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index f2d934c..459a238 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -361,7 +361,7 @@
     int i;
 
     //TODO: Optimize this with a bit vector or something
-    f = clazz->sfields;
+    f = &clazz->sfields[0];
     for (i = 0; i < clazz->sfieldCount; i++) {
         char c = f->field.signature[0];
         if (c == '[' || c == 'L') {
@@ -469,7 +469,7 @@
     ClassObject *clazz;
 
     assert(dvmIsValidObject(obj));
-    LOGV_SCAN("0x%08x %s\n", (uint)obj, obj->clazz->name);
+    LOGV_SCAN("0x%08x %s\n", (uint)obj, obj->clazz->descriptor);
 
 #if WITH_HPROF
     if (gDvm.gcHeap->hprofContext != NULL) {
diff --git a/vm/hprof/HprofHeap.c b/vm/hprof/HprofHeap.c
index 1738b57..5a35e77 100644
--- a/vm/hprof/HprofHeap.c
+++ b/vm/hprof/HprofHeap.c
@@ -30,6 +30,15 @@
 #define OBJECTS_PER_SEGMENT     ((size_t)128)
 #define BYTES_PER_SEGMENT       ((size_t)4096)
 
+/* The static field-name for the synthetic object generated to account
+ * for class Static overhead.
+ */
+#define STATIC_OVERHEAD_NAME    "$staticOverhead"
+/* The ID for the synthetic object generated to account for class
+ * Static overhead.
+ */
+#define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1))
+
 int
 hprofStartHeapDump(hprof_context_t *ctx)
 {
@@ -297,11 +306,26 @@
 
         if (clazz == gDvm.classJavaLangClass) {
             const ClassObject *thisClass = (const ClassObject *)obj;
-            int i, n;
+            int i, sFieldCount, iFieldCount;
             /* obj is a ClassObject.
              */
-            hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
+            sFieldCount = thisClass->sfieldCount;
+            if (sFieldCount != 0) {
+                int byteLength = sFieldCount*sizeof(StaticField);
+                /* Create a byte array to reflect the allocation of the
+                 * StaticField array at the end of this class.
+                 */
+                hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+                hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+                hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+                hprofAddU4ToRecord(rec, byteLength);
+                hprofAddU1ToRecord(rec, hprof_basic_byte);
+                for (i = 0; i < byteLength; i++) {
+                    hprofAddU1ToRecord(rec, 0);
+                }
+            }
 
+            hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
             hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
             hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
             hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
@@ -311,6 +335,9 @@
             hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
             hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
             if (obj == (Object *)gDvm.classJavaLangClass) {
+                // ClassObjects have their static fields appended, so
+                // aren't all the same size. But they're at least this
+                // size.
                 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
             } else {
                 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
@@ -320,34 +347,41 @@
 
             /* Static fields
              */
-            n = thisClass->sfieldCount;
-            hprofAddU2ToRecord(rec, (u2)n);
-            for (i = 0; i < n; i++) {
-                const StaticField *f = &thisClass->sfields[i];
-                hprof_basic_type t;
-                size_t size;
+            if (sFieldCount == 0) {
+                hprofAddU2ToRecord(rec, (u2)0);
+            } else {
+                hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
+                hprofAddIdToRecord(rec,
+                                   hprofLookupStringId(STATIC_OVERHEAD_NAME));
+                hprofAddU1ToRecord(rec, hprof_basic_object);
+                hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+                for (i = 0; i < sFieldCount; i++) {
+                    hprof_basic_type t;
+                    size_t size;
+                    const StaticField *f = &thisClass->sfields[i];
 
-                t = signatureToBasicTypeAndSize(f->field.signature, &size);
-                hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
-                hprofAddU1ToRecord(rec, t);
-                if (size == 1) {
-                    hprofAddU1ToRecord(rec, (u1)f->value.b);
-                } else if (size == 2) {
-                    hprofAddU2ToRecord(rec, (u2)f->value.c);
-                } else if (size == 4) {
-                    hprofAddU4ToRecord(rec, (u4)f->value.i);
-                } else if (size == 8) {
-                    hprofAddU8ToRecord(rec, (u8)f->value.j);
-                } else {
-                    assert(false);
+                    t = signatureToBasicTypeAndSize(f->field.signature, &size);
+                    hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
+                    hprofAddU1ToRecord(rec, t);
+                    if (size == 1) {
+                        hprofAddU1ToRecord(rec, (u1)f->value.b);
+                    } else if (size == 2) {
+                        hprofAddU2ToRecord(rec, (u2)f->value.c);
+                    } else if (size == 4) {
+                        hprofAddU4ToRecord(rec, (u4)f->value.i);
+                    } else if (size == 8) {
+                        hprofAddU8ToRecord(rec, (u8)f->value.j);
+                    } else {
+                        assert(false);
+                    }
                 }
             }
 
             /* Instance fields for this class (no superclass fields)
              */
-            n = thisClass->ifieldCount;
-            hprofAddU2ToRecord(rec, (u2)n);
-            for (i = 0; i < n; i++) {
+            iFieldCount = thisClass->ifieldCount;
+            hprofAddU2ToRecord(rec, (u2)iFieldCount);
+            for (i = 0; i < iFieldCount; i++) {
                 const InstField *f = &thisClass->ifields[i];
                 hprof_basic_type t;
 
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index f3cfde2..bbb8a35 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -1674,7 +1674,9 @@
      * Note that we assume that java.lang.Class does not override
      * finalize().
      */
-    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
+    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass) +
+                 sizeof(StaticField) * pHeader->staticFieldsSize,
+                                        ALLOC_DEFAULT);
     if (newClass == NULL)
         return NULL;
 
@@ -1729,11 +1731,12 @@
     /* load field definitions */
 
     /*
-     * TODO: consider over-allocating the class object and appending the
-     * static field info onto the end.  It's fixed-size and known at alloc
-     * time.  This would save a couple of native heap allocations, but it
-     * would also make heap compaction more difficult because we pass Field
-     * pointers around internally.
+     * Over-allocate the class object and append static field info
+     * onto the end.  It's fixed-size and known at alloc time.  This
+     * seems to increase zygote sharing.  Heap compaction will have to
+     * be careful if it ever tries to move ClassObject instances,
+     * because we pass Field pointers around internally. But at least
+     * now these Field pointers are in the object heap.
      */
 
     if (pHeader->staticFieldsSize != 0) {
@@ -1743,8 +1746,6 @@
         DexField field;
 
         newClass->sfieldCount = count;
-        newClass->sfields =
-            (StaticField*) calloc(count, sizeof(StaticField));
         for (i = 0; i < count; i++) {
             dexReadClassDataField(&pEncodedData, &field, &lastIndex);
             loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
@@ -2002,7 +2003,8 @@
     NULL_AND_LINEAR_FREE(clazz->ifviPool);
 
     clazz->sfieldCount = -1;
-    NULL_AND_FREE(clazz->sfields);
+    /* The sfields are attached to the ClassObject, and will be freed
+     * with it. */
 
     clazz->ifieldCount = -1;
     NULL_AND_LINEAR_FREE(clazz->ifields);
diff --git a/vm/oo/Object.c b/vm/oo/Object.c
index eff0983..fdb1a69 100644
--- a/vm/oo/Object.c
+++ b/vm/oo/Object.c
@@ -95,7 +95,7 @@
      * fields, the VM allows you to have two fields with the same name so
      * long as they have different types.
      */
-    pField = clazz->sfields;
+    pField = &clazz->sfields[0];
     for (i = 0; i < clazz->sfieldCount; i++, pField++) {
         if (strcmp(fieldName, pField->field.name) == 0 &&
             strcmp(signature, pField->field.signature) == 0)
@@ -739,4 +739,3 @@
         clazz = clazz->super;
     }
 }
-
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
index df167d5..be2c9f2 100644
--- a/vm/oo/Object.h
+++ b/vm/oo/Object.h
@@ -320,6 +320,45 @@
 };
 
 /*
+ * Generic field header.  We pass this around when we want a generic Field
+ * pointer (e.g. for reflection stuff).  Testing the accessFlags for
+ * ACC_STATIC allows a proper up-cast.
+ */
+struct Field {
+    ClassObject*    clazz;          /* class in which the field is declared */
+    const char*     name;
+    const char*     signature;      /* e.g. "I", "[C", "Landroid/os/Debug;" */
+    u4              accessFlags;
+#ifdef PROFILE_FIELD_ACCESS
+    u4              gets;
+    u4              puts;
+#endif
+};
+
+/*
+ * Static field.
+ */
+struct StaticField {
+    Field           field;          /* MUST be first item */
+    JValue          value;          /* initially set from DEX for primitives */
+};
+
+/*
+ * Instance field.
+ */
+struct InstField {
+    Field           field;          /* MUST be first item */
+
+    /*
+     * This field indicates the byte offset from the beginning of the
+     * (Object *) to the actual instance data; e.g., byteOffset==0 is
+     * the same as the object pointer (bug!), and byteOffset==4 is 4
+     * bytes farther.
+     */
+    int             byteOffset;
+};
+
+/*
  * This defines the amount of space we leave for field slots in the
  * java.lang.Class definition.  If we alter the class to have more than
  * this many fields, the VM will abort at startup.
@@ -443,10 +482,6 @@
     int             ifviPoolCount;
     int*            ifviPool;
 
-    /* static fields */
-    int             sfieldCount;
-    StaticField*    sfields;
-
     /* instance fields
      *
      * These describe the layout of the contents of a DataObject-compatible
@@ -467,6 +502,10 @@
 
     /* source file name, if known */
     const char*     sourceFile;
+
+    /* static fields */
+    int             sfieldCount;
+    StaticField     sfields[]; /* MUST be last item */
 };
 
 /*
@@ -555,45 +594,6 @@
 #endif
 };
 
-/*
- * Generic field header.  We pass this around when we want a generic Field
- * pointer (e.g. for reflection stuff).  Testing the accessFlags for
- * ACC_STATIC allows a proper up-cast.
- */
-struct Field {
-    ClassObject*    clazz;          /* class in which the field is declared */
-    const char*     name;
-    const char*     signature;      /* e.g. "I", "[C", "Landroid/os/Debug;" */
-    u4              accessFlags;
-#ifdef PROFILE_FIELD_ACCESS
-    u4              gets;
-    u4              puts;
-#endif
-};
-
-/*
- * Static field.
- */
-struct StaticField {
-    Field           field;          /* MUST be first item */
-    JValue          value;          /* initially set from DEX for primitives */
-};
-
-/*
- * Instance field.
- */
-struct InstField {
-    Field           field;          /* MUST be first item */
-
-    /*
-     * This field indicates the byte offset from the beginning of the
-     * (Object *) to the actual instance data; e.g., byteOffset==0 is
-     * the same as the object pointer (bug!), and byteOffset==4 is 4
-     * bytes farther.
-     */
-    int             byteOffset;
-};
-
 
 /*
  * Find a method within a class.  The superclass is not searched.
diff --git a/vm/reflect/Proxy.c b/vm/reflect/Proxy.c
index 31df708..4270894 100644
--- a/vm/reflect/Proxy.c
+++ b/vm/reflect/Proxy.c
@@ -47,6 +47,7 @@
 
 /* private static fields in the Proxy class */
 #define kThrowsField    0
+#define kProxySFieldCount 1
 
 
 /*
@@ -177,7 +178,9 @@
     /*
      * Allocate storage for the class object and set some basic fields.
      */
-    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
+    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass) +
+                                        kProxySFieldCount * sizeof(StaticField),
+                                        ALLOC_DEFAULT);
     if (newClass == NULL)
         goto bail;
     DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass);
@@ -228,8 +231,8 @@
      * Static field list.  We have one private field, for our list of
      * exceptions declared for each method.
      */
-    newClass->sfieldCount = 1;
-    newClass->sfields = (StaticField*) calloc(1, sizeof(StaticField));
+    assert(kProxySFieldCount == 1);
+    newClass->sfieldCount = kProxySFieldCount;
     StaticField* sfield = &newClass->sfields[kThrowsField];
     sfield->field.clazz = newClass;
     sfield->field.name = "throws";
@@ -1094,4 +1097,3 @@
     /* no match in declared throws */
     return true;
 }
-
diff --git a/vm/reflect/Reflect.c b/vm/reflect/Reflect.c
index 2e3c549..38b1ed0 100644
--- a/vm/reflect/Reflect.c
+++ b/vm/reflect/Reflect.c
@@ -306,7 +306,7 @@
     int slot;
 
     if (dvmIsStaticField(field)) {
-        slot = (StaticField*)field - clazz->sfields;
+        slot = (StaticField*)field - &clazz->sfields[0];
         assert(slot >= 0 && slot < clazz->sfieldCount);
         slot = -(slot+1);
     } else {
@@ -1255,4 +1255,3 @@
         return dvmCreateReflectMethodObject(method);
     }
 }
-