2010-06-23 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / metadata / sgen-marksweep.c
index 77b09f2e033f3bf92a4dd3012b0ffee34e2d1e82..36cdd006b16e64011db975e7e7221c14e9f67c49 100644 (file)
@@ -1,6 +1,7 @@
 #include <math.h>
 
 #define MS_BLOCK_SIZE  (16*1024)
+#define MAJOR_SECTION_SIZE     MS_BLOCK_SIZE
 
 /*
  * Don't allocate single blocks, but alloc a contingent of this many
@@ -17,7 +18,7 @@
 
 #define MS_BLOCK_FREE  (MS_BLOCK_SIZE - MS_BLOCK_SKIP)
 
-#define MS_NUM_MARK_WORDS(c)   (((c) + sizeof (mword) * 8 - 1) / (sizeof (mword) * 8))
+#define MS_NUM_MARK_WORDS      ((MS_BLOCK_SIZE / ALLOC_ALIGN + sizeof (mword) * 8 - 1) / (sizeof (mword) * 8))
 
 #if MAX_SMALL_OBJ_SIZE > MS_BLOCK_FREE / 2
 #error MAX_SMALL_OBJ_SIZE must be at most (MS_BLOCK_SIZE - MS_BLOCK_SKIP) / 2
@@ -27,13 +28,14 @@ typedef struct _MSBlockInfo MSBlockInfo;
 struct _MSBlockInfo {
        int obj_size;
        gboolean pinned;
+       gboolean has_references;
        char *block;
        void **free_list;
        MSBlockInfo *next_free;
        MSBlockInfo *next;
        int pin_queue_start;
        int pin_queue_end;
-       mword mark_words [1];
+       mword mark_words [MS_NUM_MARK_WORDS];
 };
 
 #define MS_BLOCK_OBJ(b,i)      ((b)->block + MS_BLOCK_SKIP + (b)->obj_size * (i))
@@ -46,14 +48,15 @@ typedef struct {
 
 #define MS_BLOCK_OBJ_INDEX(o,b)        (((char*)(o) - ((b)->block + MS_BLOCK_SKIP)) / (b)->obj_size)
 
-#define MS_CALC_MARK_BIT(w,b,i)        do {            \
-               if (sizeof (mword) == 4) {      \
-                       (w) = (i) >> 5;         \
-                       (b) = (i) & 31;         \
-               } else {                        \
-                       (w) = (i) >> 6;         \
-                       (b) = (i) & 63;         \
-               }                               \
+#define MS_CALC_MARK_BIT(w,b,o,bl)     do {                            \
+               int i = ((char*)(o) - (bl)->block) >> ALLOC_ALIGN_BITS; \
+               if (sizeof (mword) == 4) {                              \
+                       (w) = i >> 5;                                   \
+                       (b) = i & 31;                                   \
+               } else {                                                \
+                       (w) = i >> 6;                                   \
+                       (b) = i & 63;                                   \
+               }                                                       \
        } while (0)
 
 #define MS_MARK_BIT(bl,w,b)    ((bl)->mark_words [(w)] & (1L << (b)))
@@ -63,11 +66,6 @@ typedef struct {
 
 #define MS_BLOCK_OBJ_SIZE_FACTOR       (sqrt (2.0))
 
-#define MIN_MINOR_COLLECTION_SECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 3 / MS_BLOCK_SIZE)
-
-static int minor_collection_section_allowance;
-static int minor_collection_sections_alloced = 0;
-
 /*
  * This way we can lookup block object size indexes for sizes up to
  * 256 bytes with a single load.
@@ -78,12 +76,16 @@ static int *block_obj_sizes;
 static int num_block_obj_sizes;
 static int fast_block_obj_size_indexes [MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES];
 
+#define MS_BLOCK_FLAG_PINNED   1
+#define MS_BLOCK_FLAG_REFS     2
+
+#define MS_BLOCK_TYPE_MAX      4
+
 /* all blocks in the system */
 static MSBlockInfo *all_blocks;
 static int num_major_sections = 0;
 /* one free block list for each block object size */
-static MSBlockInfo **free_non_pinned_blocks;
-static MSBlockInfo **free_pinned_blocks;
+static MSBlockInfo **free_block_lists [MS_BLOCK_TYPE_MAX];
 
 static long stat_major_blocks_alloced = 0;
 static long stat_major_blocks_freed = 0;
@@ -99,7 +101,7 @@ ms_find_block_obj_size_index (int size)
        g_assert_not_reached ();
 }
 
-#define FREE_BLOCKS(pinned) ((pinned) ? free_pinned_blocks : free_non_pinned_blocks)
+#define FREE_BLOCKS(p,r) (free_block_lists [((p) ? MS_BLOCK_FLAG_PINNED : 0) | ((r) ? MS_BLOCK_FLAG_REFS : 0)])
 
 #define MS_BLOCK_OBJ_SIZE_INDEX(s)                             \
        (((s)+7)>>3 < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES ?      \
@@ -142,6 +144,8 @@ ms_get_empty_block (void)
 
        g_assert (!((mword)block & (MS_BLOCK_SIZE - 1)));
 
+       UPDATE_HEAP_BOUNDARIES (block, (char*)block + MS_BLOCK_SIZE);
+
        return block;
 }
 
@@ -156,6 +160,9 @@ ms_free_block (void *block)
        ++num_empty_blocks;
 }
 
+//#define MARKSWEEP_CONSISTENCY_CHECK
+
+#ifdef MARKSWEEP_CONSISTENCY_CHECK
 static void
 check_block_free_list (MSBlockInfo *block, int size, gboolean pinned)
 {
@@ -187,7 +194,6 @@ consistency_check (void)
        /* check all blocks */
        for (block = all_blocks; block; block = block->next) {
                int count = MS_BLOCK_FREE / block->obj_size;
-               int num_mark_words = MS_NUM_MARK_WORDS (count);
                int num_free = 0;
                void **free;
 
@@ -209,27 +215,27 @@ consistency_check (void)
                g_assert (num_free == 0);
 
                /* check all mark words are zero */
-               for (i = 0; i < num_mark_words; ++i)
+               for (i = 0; i < MS_NUM_MARK_WORDS; ++i)
                        g_assert (block->mark_words [i] == 0);
        }
 
        /* check free blocks */
        for (i = 0; i < num_block_obj_sizes; ++i) {
-               check_block_free_list (free_non_pinned_blocks [i], block_obj_sizes [i], FALSE);
-               check_block_free_list (free_pinned_blocks [i], block_obj_sizes [i], TRUE);
+               int j;
+               for (j = 0; j < MS_BLOCK_TYPE_MAX; ++j)
+                       check_block_free_list (free_block_lists [j][i], block_obj_sizes [i], j & MS_BLOCK_FLAG_PINNED);
        }
 }
+#endif
 
 static void
-ms_alloc_block (int size_index, gboolean pinned)
+ms_alloc_block (int size_index, gboolean pinned, gboolean has_references)
 {
        int size = block_obj_sizes [size_index];
        int count = MS_BLOCK_FREE / size;
-       int num_mark_words = MS_NUM_MARK_WORDS (count);
-       int block_info_size = sizeof (MSBlockInfo) + sizeof (mword) * (num_mark_words - 1);
-       MSBlockInfo *info = get_internal_mem (block_info_size, INTERNAL_MEM_MS_BLOCK_INFO);
+       MSBlockInfo *info = get_internal_mem (sizeof (MSBlockInfo), INTERNAL_MEM_MS_BLOCK_INFO);
        MSBlockHeader *header;
-       MSBlockInfo **free_blocks = FREE_BLOCKS (pinned);
+       MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, has_references);
        char *obj_start;
        int i;
 
@@ -237,6 +243,7 @@ ms_alloc_block (int size_index, gboolean pinned)
 
        info->obj_size = size;
        info->pinned = pinned;
+       info->has_references = has_references;
        info->block = ms_get_empty_block ();
 
        header = (MSBlockHeader*) info->block;
@@ -271,15 +278,15 @@ obj_is_from_pinned_alloc (char *obj)
 }
 
 static void*
-alloc_obj (int size, gboolean pinned)
+alloc_obj (int size, gboolean pinned, gboolean has_references)
 {
        int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
-       MSBlockInfo **free_blocks = FREE_BLOCKS (pinned);
+       MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, has_references);
        MSBlockInfo *block;
        void *obj;
 
        if (!free_blocks [size_index])
-               ms_alloc_block (size_index, pinned);
+               ms_alloc_block (size_index, pinned, has_references);
 
        block = free_blocks [size_index];
        DEBUG (9, g_assert (block));
@@ -303,14 +310,14 @@ alloc_obj (int size, gboolean pinned)
 }
 
 static void*
-ms_alloc_obj (int size)
+ms_alloc_obj (int size, gboolean has_references)
 {
-       return alloc_obj (size, FALSE);
+       return alloc_obj (size, FALSE, has_references);
 }
 
 /* FIXME: inline fast path */
-#define MAJOR_GET_COPY_OBJECT_SPACE(dest, size) do {   \
-               (dest) = ms_alloc_obj ((size));         \
+#define MAJOR_GET_COPY_OBJECT_SPACE(dest, size, refs) do {     \
+               (dest) = ms_alloc_obj ((size), (refs));         \
        } while (0)
 
 /*
@@ -324,10 +331,10 @@ free_object (char *obj, size_t size, gboolean pinned)
        int word, bit;
        DEBUG (9, g_assert ((pinned && block->pinned) || (!pinned && !block->pinned)));
        DEBUG (9, g_assert (MS_OBJ_ALLOCED (obj, block)));
-       MS_CALC_MARK_BIT (word, bit, MS_BLOCK_OBJ_INDEX (obj, block));
+       MS_CALC_MARK_BIT (word, bit, obj, block);
        DEBUG (9, g_assert (!MS_MARK_BIT (block, word, bit)));
        if (!block->free_list) {
-               MSBlockInfo **free_blocks = FREE_BLOCKS (pinned);
+               MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, block->has_references);
                int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
                DEBUG (9, g_assert (!block->next_free));
                block->next_free = free_blocks [size_index];
@@ -346,9 +353,9 @@ major_free_non_pinned_object (char *obj, size_t size)
 
 /* size is a multiple of ALLOC_ALIGN */
 static void*
-major_alloc_small_pinned_obj (size_t size)
+major_alloc_small_pinned_obj (size_t size, gboolean has_references)
 {
-       return alloc_obj (size, TRUE);
+       return alloc_obj (size, TRUE, has_references);
 }
 
 static void
@@ -361,10 +368,16 @@ free_pinned_object (char *obj, size_t size)
  * size is already rounded up and we hold the GC lock.
  */
 static void*
-alloc_degraded (MonoVTable *vtable, size_t size)
+major_alloc_degraded (MonoVTable *vtable, size_t size)
 {
-       void *obj = alloc_obj (size, FALSE);
+       void *obj;
+       int old_num_sections = num_major_sections;
+       obj = alloc_obj (size, FALSE, vtable->klass->has_references);
        *(MonoVTable**)obj = vtable;
+       HEAVY_STAT (++stat_objects_alloced_degraded);
+       HEAVY_STAT (stat_bytes_alloced_degraded += size);
+       g_assert (num_major_sections >= old_num_sections);
+       minor_collection_sections_alloced += num_major_sections - old_num_sections;
        return obj;
 }
 
@@ -396,7 +409,7 @@ major_is_object_live (char *obj)
        /* now we know it's in a major block */
        block = MS_BLOCK_FOR_OBJ (obj);
        DEBUG (9, g_assert (!block->pinned));
-       MS_CALC_MARK_BIT (word, bit, MS_BLOCK_OBJ_INDEX (obj, block));
+       MS_CALC_MARK_BIT (word, bit, obj, block);
        return MS_MARK_BIT (block, word, bit) ? TRUE : FALSE;
 }
 
@@ -458,24 +471,24 @@ major_dump_heap (void)
        }
 }
 
-#define MS_MARK_INDEX_IN_BLOCK_AND_ENQUEUE_CHECKED(obj,block,index) do { \
+#define MS_MARK_OBJECT_AND_ENQUEUE_CHECKED(obj,block) do {             \
                int __word, __bit;                                      \
-               MS_CALC_MARK_BIT (__word, __bit, (index));              \
-               DEBUG (9, g_assert ((obj) == MS_BLOCK_OBJ ((block), (index)))); \
+               MS_CALC_MARK_BIT (__word, __bit, (obj), (block));       \
                if (!MS_MARK_BIT ((block), __word, __bit) && MS_OBJ_ALLOCED ((obj), (block))) { \
                        MS_SET_MARK_BIT ((block), __word, __bit);       \
-                       GRAY_OBJECT_ENQUEUE ((obj));                    \
+                       if ((block)->has_references)                    \
+                               GRAY_OBJECT_ENQUEUE ((obj));            \
                        binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), safe_object_get_size ((MonoObject*)(obj))); \
                }                                                       \
        } while (0)
-#define MS_MARK_INDEX_IN_BLOCK_AND_ENQUEUE(obj,block,index) do {       \
+#define MS_MARK_OBJECT_AND_ENQUEUE(obj,block) do {                     \
                int __word, __bit;                                      \
-               MS_CALC_MARK_BIT (__word, __bit, (index));              \
-               DEBUG (9, g_assert ((obj) == MS_BLOCK_OBJ ((block), (index)))); \
-               g_assert (MS_OBJ_ALLOCED ((obj), (block)));             \
+               MS_CALC_MARK_BIT (__word, __bit, (obj), (block));       \
+               DEBUG (9, g_assert (MS_OBJ_ALLOCED ((obj), (block))));  \
                if (!MS_MARK_BIT ((block), __word, __bit)) {            \
                        MS_SET_MARK_BIT ((block), __word, __bit);       \
-                       GRAY_OBJECT_ENQUEUE ((obj));                    \
+                       if ((block)->has_references)                    \
+                               GRAY_OBJECT_ENQUEUE ((obj));            \
                        binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), safe_object_get_size ((MonoObject*)(obj))); \
                }                                                       \
        } while (0)
@@ -486,8 +499,8 @@ major_copy_or_mark_object (void **ptr)
        void *obj = *ptr;
        mword objsize;
        MSBlockInfo *block;
-       int index;
-       int count;
+
+       HEAVY_STAT (++stat_copy_object_called_major);
 
        DEBUG (9, g_assert (obj));
        DEBUG (9, g_assert (current_collection_generation == GENERATION_OLD));
@@ -503,6 +516,8 @@ major_copy_or_mark_object (void **ptr)
                if (object_is_pinned (obj))
                        return;
 
+               HEAVY_STAT (++stat_objects_copied_major);
+
                obj = copy_object_no_checks (obj);
                *ptr = obj;
 
@@ -513,9 +528,7 @@ major_copy_or_mark_object (void **ptr)
                 * re-fetch it.
                 */
                block = MS_BLOCK_FOR_OBJ (obj);
-               index = MS_BLOCK_OBJ_INDEX (obj, block);
-               DEBUG (9, g_assert (obj == MS_BLOCK_OBJ (block, index)));
-               MS_CALC_MARK_BIT (word, bit, (index));
+               MS_CALC_MARK_BIT (word, bit, obj, block);
                DEBUG (9, g_assert (!MS_MARK_BIT (block, word, bit)));
                MS_SET_MARK_BIT (block, word, bit);
                return;
@@ -530,15 +543,13 @@ major_copy_or_mark_object (void **ptr)
                        return;
                binary_protocol_pin (obj, (gpointer)LOAD_VTABLE (obj), safe_object_get_size ((MonoObject*)obj));
                pin_object (obj);
+               /* FIXME: only enqueue if object has references */
                GRAY_OBJECT_ENQUEUE (obj);
                return;
        }
 
        block = MS_BLOCK_FOR_OBJ (obj);
-       index = MS_BLOCK_OBJ_INDEX (obj, block);
-       count = MS_BLOCK_FREE / block->obj_size;
-       DEBUG (9, g_assert (index >= 0 && index < count));
-       MS_MARK_INDEX_IN_BLOCK_AND_ENQUEUE (obj, block, index);
+       MS_MARK_OBJECT_AND_ENQUEUE (obj, block);
 }
 
 static void
@@ -553,7 +564,7 @@ mark_pinned_objects_in_block (MSBlockInfo *block)
                DEBUG (9, g_assert (index >= 0 && index < count));
                if (index == last_index)
                        continue;
-               MS_MARK_INDEX_IN_BLOCK_AND_ENQUEUE_CHECKED (MS_BLOCK_OBJ (block, index), block, index);
+               MS_MARK_OBJECT_AND_ENQUEUE_CHECKED (MS_BLOCK_OBJ (block, index), block);
                last_index = index;
        }
 }
@@ -561,54 +572,48 @@ mark_pinned_objects_in_block (MSBlockInfo *block)
 static void
 major_sweep (void)
 {
-       MSBlockInfo *empty_blocks = NULL;
        MSBlockInfo **iter;
        int i;
 
+       /* clear all the free lists */
+       for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i) {
+               MSBlockInfo **free_blocks = free_block_lists [i];
+               int j;
+               for (j = 0; j < num_block_obj_sizes; ++j)
+                       free_blocks [j] = NULL;
+       }
+
        /* traverse all blocks, free and zero unmarked objects */
        iter = &all_blocks;
        while (*iter) {
                MSBlockInfo *block = *iter;
                int count = MS_BLOCK_FREE / block->obj_size;
                gboolean have_live = FALSE;
-               int word_index = 0;
-               int obj_index = 0;
+               int obj_index;
 
                block->free_list = NULL;
 
-               do {
-                       mword word = block->mark_words [word_index];
+               for (obj_index = 0; obj_index < count; ++obj_index) {
+                       int word, bit;
+                       void *obj = MS_BLOCK_OBJ (block, obj_index);
 
-                       if (word == (mword)-1L) {
-                               obj_index += 8 * sizeof (mword);
+                       MS_CALC_MARK_BIT (word, bit, obj, block);
+                       if (MS_MARK_BIT (block, word, bit)) {
+                               DEBUG (9, g_assert (MS_OBJ_ALLOCED (obj, block)));
                                have_live = TRUE;
                        } else {
-                               int num_bits = MIN (count - obj_index, 8 * sizeof (mword));
-                               for (i = 0; i < num_bits; ++i) {
-                                       void *obj = MS_BLOCK_OBJ (block, obj_index);
-                                       if (word & 1) {
-                                               DEBUG (9, g_assert (MS_OBJ_ALLOCED (obj, block)));
-                                               have_live = TRUE;
-                                       } else {
-                                               /* an unmarked object */
-                                               void *obj = MS_BLOCK_OBJ (block, obj_index);
-                                               if (MS_OBJ_ALLOCED (obj, block)) {
-                                                       binary_protocol_empty (obj, block->obj_size);
-                                                       memset (obj, 0, block->obj_size);
-                                               }
-                                               *(void**)obj = block->free_list;
-                                               block->free_list = obj;
-                                       }
-                                       word >>= 1;
-                                       ++obj_index;
+                               /* an unmarked object */
+                               if (MS_OBJ_ALLOCED (obj, block)) {
+                                       binary_protocol_empty (obj, block->obj_size);
+                                       memset (obj, 0, block->obj_size);
                                }
+                               *(void**)obj = block->free_list;
+                               block->free_list = obj;
                        }
+               }
 
-                       /* reset mark bits */
-                       block->mark_words [word_index] = 0;
-
-                       ++word_index;
-               } while (obj_index < count);
+               /* reset mark bits */
+               memset (block->mark_words, 0, sizeof (mword) * MS_NUM_MARK_WORDS);
 
                /*
                 * FIXME: reverse free list so that it's in address
@@ -617,268 +622,80 @@ major_sweep (void)
 
                if (have_live) {
                        iter = &block->next;
-                       continue;
-               }
 
-               /*
-                * blocks without live objects are removed from the
-                * block list and marked with obj_size = -1
-                */
-               *iter = block->next;
-               block->next = empty_blocks;
-               empty_blocks = block;
+                       /*
+                        * If there are free slots in the block, add
+                        * the block to the corresponding free list.
+                        */
+                       if (block->free_list) {
+                               MSBlockInfo **free_blocks = FREE_BLOCKS (block->pinned, block->has_references);
+                               int index = MS_BLOCK_OBJ_SIZE_INDEX (block->obj_size);
+                               block->next_free = free_blocks [index];
+                               free_blocks [index] = block;
+                       }
+               } else {
+                       /*
+                        * Blocks without live objects are removed from the
+                        * block list and freed.
+                        */
+                       *iter = block->next;
 
-               block->obj_size = -1;
-       }
+                       ms_free_block (block->block);
+                       free_internal_mem (block, INTERNAL_MEM_MS_BLOCK_INFO);
 
-       /* if there are no blocks to be freed, we're done */
-       if (!empty_blocks)
-               return;
+                       --num_major_sections;
 
-       /* go through all free lists and remove the blocks to be freed */
-       for (i = 0; i < num_block_obj_sizes; ++i) {
-               int pinned;
-               for (pinned = 0; pinned <= 1; ++pinned) {
-                       MSBlockInfo **free_blocks = FREE_BLOCKS (pinned);
-                       iter = &(free_blocks [i]);
-                       while (*iter) {
-                               MSBlockInfo *block = *iter;
-                               if (block->obj_size < 0)
-                                       *iter = block->next_free;
-                               else
-                                       iter = &block->next_free;
-                       }
+                       ++stat_major_blocks_freed;
                }
        }
+}
 
-       /* now free the blocks */
-       while (empty_blocks) {
-               MSBlockInfo *next = empty_blocks->next;
-
-               ms_free_block (empty_blocks->block);
-               free_internal_mem (empty_blocks, INTERNAL_MEM_MS_BLOCK_INFO);
-
-               empty_blocks = next;
+static int count_pinned_ref;
+static int count_pinned_nonref;
+static int count_nonpinned_ref;
+static int count_nonpinned_nonref;
 
-               --num_major_sections;
+static void
+count_nonpinned_callback (char *obj, size_t size, void *data)
+{
+       MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
 
-               ++stat_major_blocks_freed;
-       }
+       if (vtable->klass->has_references)
+               ++count_nonpinned_ref;
+       else
+               ++count_nonpinned_nonref;
 }
 
 static void
-major_do_collection (const char *reason)
+count_pinned_callback (char *obj, size_t size, void *data)
 {
-       LOSObject *bigobj, *prevbo;
-       MSBlockInfo *block;
-       TV_DECLARE (all_atv);
-       TV_DECLARE (all_btv);
-       TV_DECLARE (atv);
-       TV_DECLARE (btv);
-       /* FIXME: only use these values for the precise scan
-        * note that to_space pointers should be excluded anyway...
-        */
-       char *heap_start = NULL;
-       char *heap_end = (char*)-1;
-       int old_num_major_sections = num_major_sections;
-       int num_major_sections_saved, save_target, allowance_target;
-
-       //consistency_check ();
-
-       init_stats ();
-       binary_protocol_collection (GENERATION_OLD);
-       check_scan_starts ();
-       gray_object_queue_init ();
-
-       degraded_mode = 0;
-       DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
-       num_major_gcs++;
-       mono_stats.major_gc_count ++;
-
-       /* world must be stopped already */
-       TV_GETTIME (all_atv);
-       TV_GETTIME (atv);
+       MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
 
-       /* Pinning depends on this */
-       clear_nursery_fragments (nursery_next);
-
-       TV_GETTIME (btv);
-       time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
-
-       if (xdomain_checks)
-               check_for_xdomain_refs ();
-
-       nursery_section->next_data = nursery_real_end;
-       /* we should also coalesce scanning from sections close to each other
-        * and deal with pointers outside of the sections later.
-        */
-       /* The remsets are not useful for a major collection */
-       clear_remsets ();
-
-       TV_GETTIME (atv);
-       init_pinning ();
-       DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
-       pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
-       optimize_pin_queue (0);
-
-       /*
-        * pin_queue now contains all candidate pointers, sorted and
-        * uniqued.  We must do two passes now to figure out which
-        * objects are pinned.
-        *
-        * The first is to find within the pin_queue the area for each
-        * section.  This requires that the pin_queue be sorted.  We
-        * also process the LOS objects and pinned chunks here.
-        *
-        * The second, destructive, pass is to reduce the section
-        * areas to pointers to the actually pinned objects.
-        */
-       DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
-       /* first pass for the sections */
-       find_section_pin_queue_start_end (nursery_section);
-       for (block = all_blocks; block; block = block->next) {
-               find_optimized_pin_queue_area (block->block + MS_BLOCK_SKIP, block->block + MS_BLOCK_SIZE,
-                               &block->pin_queue_start, &block->pin_queue_end);
-       }
-       /* identify possible pointers to the insize of large objects */
-       DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
-       for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
-               int start, end;
-               find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &start, &end);
-               if (start != end) {
-                       pin_object (bigobj->data);
-                       GRAY_OBJECT_ENQUEUE (bigobj->data);
-                       if (heap_dump_file)
-                               pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
-                       DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
-               }
-       }
-       /* second pass for the sections */
-       pin_objects_in_section (nursery_section);
-       for (block = all_blocks; block; block = block->next)
-               mark_pinned_objects_in_block (block);
-
-       TV_GETTIME (btv);
-       time_major_pinning += TV_ELAPSED_MS (atv, btv);
-       DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
-       DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
-
-       drain_gray_stack ();
-
-       TV_GETTIME (atv);
-       time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
-
-       /* registered roots, this includes static fields */
-       scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL);
-       scan_from_registered_roots (major_copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER);
-       TV_GETTIME (btv);
-       time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
-
-       /* Threads */
-       /* FIXME: This is the wrong place for this, because it does
-          pinning */
-       scan_thread_data (heap_start, heap_end, TRUE);
-       TV_GETTIME (atv);
-       time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
-
-       TV_GETTIME (btv);
-       time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
-
-       /* scan the list of objects ready for finalization */
-       scan_finalizer_entries (major_copy_or_mark_object, fin_ready_list);
-       scan_finalizer_entries (major_copy_or_mark_object, critical_fin_list);
-       TV_GETTIME (atv);
-       time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
-       DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
-
-       TV_GETTIME (btv);
-       time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
-
-       /* all the objects in the heap */
-       finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
-       TV_GETTIME (atv);
-       time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
-
-       /* sweep the big objects list */
-       prevbo = NULL;
-       for (bigobj = los_object_list; bigobj;) {
-               if (object_is_pinned (bigobj->data)) {
-                       unpin_object (bigobj->data);
-               } else {
-                       LOSObject *to_free;
-                       /* not referenced anywhere, so we can free it */
-                       if (prevbo)
-                               prevbo->next = bigobj->next;
-                       else
-                               los_object_list = bigobj->next;
-                       to_free = bigobj;
-                       bigobj = bigobj->next;
-                       free_large_object (to_free);
-                       continue;
-               }
-               prevbo = bigobj;
-               bigobj = bigobj->next;
-       }
-
-       major_sweep ();
-
-       TV_GETTIME (btv);
-       time_major_sweep += TV_ELAPSED_MS (atv, btv);
-
-       /* walk the pin_queue, build up the fragment list of free memory, unmark
-        * pinned objects as we go, memzero() the empty fragments so they are ready for the
-        * next allocations.
-        */
-       build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
-
-       TV_GETTIME (atv);
-       time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
-
-       TV_GETTIME (all_btv);
-       mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
-
-       if (heap_dump_file)
-               dump_heap ("major", num_major_gcs - 1, reason);
-
-       /* prepare the pin queue for the next collection */
-       next_pin_slot = 0;
-       if (fin_ready_list || critical_fin_list) {
-               DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
-               mono_gc_finalize_notify ();
-       }
-       pin_stats_reset ();
-
-       g_assert (gray_object_queue_is_empty ());
-
-       commit_stats (GENERATION_OLD);
-
-       num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 1);
+       if (vtable->klass->has_references)
+               ++count_pinned_ref;
+       else
+               ++count_pinned_nonref;
+}
 
-       save_target = num_major_sections / 2;
-       /*
-        * We aim to allow the allocation of as many sections as is
-        * necessary to reclaim save_target sections in the next
-        * collection.  We assume the collection pattern won't change.
-        * In the last cycle, we had num_major_sections_saved for
-        * minor_collection_sections_alloced.  Assuming things won't
-        * change, this must be the same ratio as save_target for
-        * allowance_target, i.e.
-        *
-        *    num_major_sections_saved            save_target
-        * --------------------------------- == ----------------
-        * minor_collection_sections_alloced    allowance_target
-        *
-        * hence:
-        */
-       allowance_target = save_target * minor_collection_sections_alloced / num_major_sections_saved;
+static void __attribute__ ((unused))
+count_ref_nonref_objs (void)
+{
+       int total;
 
-       minor_collection_section_allowance = MAX (MIN (allowance_target, num_major_sections), MIN_MINOR_COLLECTION_SECTION_ALLOWANCE);
+       count_pinned_ref = 0;
+       count_pinned_nonref = 0;
+       count_nonpinned_ref = 0;
+       count_nonpinned_nonref = 0;
 
-       minor_collection_sections_alloced = 0;
+       major_iterate_objects (TRUE, FALSE, count_nonpinned_callback, NULL);
+       major_iterate_objects (FALSE, TRUE, count_pinned_callback, NULL);
 
-       check_scan_starts ();
+       total = count_pinned_nonref + count_nonpinned_nonref + count_pinned_ref + count_nonpinned_ref;
 
-       //consistency_check ();
+       g_print ("ref: %d pinned %d non-pinned   non-ref: %d pinned %d non-pinned  --  %.1f\n",
+                       count_pinned_ref, count_nonpinned_ref,
+                       count_pinned_nonref, count_nonpinned_nonref,
+                       (count_pinned_nonref + count_nonpinned_nonref) * 100.0 / total);
 }
 
 static int
@@ -923,8 +740,8 @@ major_init (void)
        }
        */
 
-       free_pinned_blocks = get_internal_mem (sizeof (MSBlockInfo*) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES);
-       free_non_pinned_blocks = get_internal_mem (sizeof (MSBlockInfo*) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES);
+       for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i)
+               free_block_lists [i] = get_internal_mem (sizeof (MSBlockInfo*) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES);
 
        for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES; ++i)
                fast_block_obj_size_indexes [i] = ms_find_block_obj_size_index (i * 8);
@@ -933,8 +750,6 @@ major_init (void)
 
        mono_counters_register ("# major blocks allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_alloced);
        mono_counters_register ("# major blocks freed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_freed);
-
-       minor_collection_section_allowance = MIN_MINOR_COLLECTION_SECTION_ALLOWANCE;
 }
 
 /* only valid during minor collections */
@@ -943,7 +758,9 @@ static int old_num_major_sections;
 static void
 major_start_nursery_collection (void)
 {
-       //consistency_check ();
+#ifdef MARKSWEEP_CONSISTENCY_CHECK
+       consistency_check ();
+#endif
 
        old_num_major_sections = num_major_sections;
 }
@@ -953,16 +770,37 @@ major_finish_nursery_collection (void)
 {
        int sections_alloced;
 
-       //consistency_check ();
+#ifdef MARKSWEEP_CONSISTENCY_CHECK
+       consistency_check ();
+#endif
 
        sections_alloced = num_major_sections - old_num_major_sections;
        minor_collection_sections_alloced += sections_alloced;
 }
 
-static gboolean
-major_need_major_collection (void)
+static void
+major_find_pin_queue_start_ends (void)
+{
+       MSBlockInfo *block;
+
+       for (block = all_blocks; block; block = block->next) {
+               find_optimized_pin_queue_area (block->block + MS_BLOCK_SKIP, block->block + MS_BLOCK_SIZE,
+                               &block->pin_queue_start, &block->pin_queue_end);
+       }
+}
+
+static void
+major_pin_objects (void)
+{
+       MSBlockInfo *block;
+
+       for (block = all_blocks; block; block = block->next)
+               mark_pinned_objects_in_block (block);
+}
+
+static void
+major_init_to_space (void)
 {
-       return minor_collection_sections_alloced > minor_collection_section_allowance;
 }
 
 static void