#include "metadata/object-internals.h"
#include "metadata/threads.h"
#include "metadata/sgen-gc.h"
+#include "metadata/sgen-cardtable.h"
+#include "metadata/sgen-protocol.h"
#include "metadata/sgen-archdep.h"
+#include "metadata/sgen-bridge.h"
#include "metadata/mono-gc.h"
#include "metadata/method-builder.h"
#include "metadata/profiler-private.h"
*/
static int gc_initialized = 0;
-/* If set, do a minor collection before every allocation */
-static gboolean collect_before_allocs = FALSE;
+/* If set, do a minor collection before every X allocation */
+static guint32 collect_before_allocs = 0;
/* If set, do a heap consistency check before each minor collection */
static gboolean consistency_check_at_minor_collection = FALSE;
/* If set, check that there are no references to the domain left at domain unload */
/* If not null, dump the heap after each collection into this file */
static FILE *heap_dump_file = NULL;
/* If set, mark stacks conservatively, even if precise marking is possible */
-static gboolean conservative_stack_mark = TRUE;
+static gboolean conservative_stack_mark = FALSE;
/* If set, do a plausibility check on the scan_starts before and after
each collection */
static gboolean do_scan_starts_check = FALSE;
static int stat_wbarrier_object_copy = 0;
#endif
+static long long stat_pinned_objects = 0;
+
static long long time_minor_pre_collection_fragment_clear = 0;
static long long time_minor_pinning = 0;
static long long time_minor_scan_remsets = 0;
+static long long time_minor_scan_card_table = 0;
static long long time_minor_scan_pinned = 0;
static long long time_minor_scan_registered_roots = 0;
static long long time_minor_scan_thread_data = 0;
#define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
-static int gc_debug_level = 0;
-static FILE* gc_debug_file;
+int gc_debug_level = 0;
+FILE* gc_debug_file;
/*
void
*/
#define USER_CONFIG 1
-#define TV_DECLARE(name) gint64 name
-#define TV_GETTIME(tv) tv = mono_100ns_ticks ()
-#define TV_ELAPSED(start,end) (int)((end-start) / 10)
-#define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
+#define TV_DECLARE SGEN_TV_DECLARE
+#define TV_GETTIME SGEN_TV_GETTIME
+#define TV_ELAPSED SGEN_TV_ELAPSED
+#define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
#define safe_object_get_size mono_sgen_safe_object_get_size
+const char*
+mono_sgen_safe_name (void* obj)
+{
+ return safe_name (obj);
+}
+
/*
* ######################################################################
* ######## Global data.
static LOCK_DECLARE (interruption_mutex);
static LOCK_DECLARE (global_remset_mutex);
+static LOCK_DECLARE (pin_queue_mutex);
#define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
#define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
+#define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
+#define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
+
typedef struct _FinalizeEntry FinalizeEntry;
struct _FinalizeEntry {
FinalizeEntry *next;
void *value;
} Ephemeron;
-enum {
- GENERATION_NURSERY,
- GENERATION_OLD,
- GENERATION_MAX
-};
-
int current_collection_generation = -1;
/*
static mword roots_size = 0; /* amount of memory in the root set */
static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
+#define GC_ROOT_NUM 32
+typedef struct {
+ int count;
+ void *objects [GC_ROOT_NUM];
+ int root_types [GC_ROOT_NUM];
+ uintptr_t extra_info [GC_ROOT_NUM];
+} GCRootReport;
+
+static void
+notify_gc_roots (GCRootReport *report)
+{
+ if (!report->count)
+ return;
+ mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
+ report->count = 0;
+}
+
+static void
+add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
+{
+ if (report->count == GC_ROOT_NUM)
+ notify_gc_roots (report);
+ report->objects [report->count] = object;
+ report->root_types [report->count] = rtype;
+ report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
+}
+
/*
* The current allocation cursors
* We allocate objects in the nursery.
static void *moved_objects [MOVED_OBJECTS_NUM];
static int moved_objects_idx = 0;
+/* Vtable of the objects used to fill out nursery fragments before a collection */
+static MonoVTable *array_fill_vtable;
+
+#ifdef SGEN_DEBUG_INTERNAL_ALLOC
+pthread_t main_gc_thread = NULL;
+#endif
+
/*
* ######################################################################
- * ######## Macros and function declarations.
+ * ######## Heap size accounting
* ######################################################################
*/
+/*heap limits*/
+static mword max_heap_size = ((mword)0)- ((mword)1);
+static mword allocated_heap;
+
+/*Object was pinned during the current collection*/
+static mword objects_pinned;
+
+void
+mono_sgen_release_space (mword size, int space)
+{
+ allocated_heap -= size;
+}
+
+static size_t
+available_free_space (void)
+{
+ return max_heap_size - MIN (allocated_heap, max_heap_size);
+}
+
+gboolean
+mono_sgen_try_alloc_space (mword size, int space)
+{
+ if (available_free_space () < size)
+ return FALSE;
-#define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
+ allocated_heap += size;
+ return TRUE;
+}
+
+static void
+init_heap_size_limits (glong max_heap)
+{
+ if (max_heap == 0)
+ return;
+
+ if (max_heap < nursery_size * 4) {
+ fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
+ exit (1);
+ }
+ max_heap_size = max_heap - nursery_size;
+}
+
+/*
+ * ######################################################################
+ * ######## Macros and function declarations.
+ * ######################################################################
+ */
inline static void*
align_pointer (void *ptr)
typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
/* forward declarations */
-static int stop_world (void);
-static int restart_world (void);
+static int stop_world (int generation);
+static int restart_world (int generation);
static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
+static void report_finalizer_roots (void);
+static void report_registered_roots (void);
static void find_pinning_ref_from_thread (char *obj, size_t size);
static void update_current_thread_stack (void *start);
static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
-static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
+static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
static void null_links_for_domain (MonoDomain *domain, int generation);
static gboolean search_fragment_for_size (size_t size);
static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
static void clear_remsets (void);
static void clear_tlabs (void);
static void sort_addresses (void **array, int size);
-static void drain_gray_stack (GrayQueue *queue);
+static gboolean drain_gray_stack (GrayQueue *queue, int max_objs);
static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
-static gboolean need_major_collection (void);
+static gboolean need_major_collection (mword space_needed);
static void major_collection (const char *reason);
static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
static void null_ephemerons_for_domain (MonoDomain *domain);
-SgenMajorCollector major;
+SgenMajorCollector major_collector;
-#include "sgen-protocol.c"
#include "sgen-pinning.c"
#include "sgen-pinning-stats.c"
#include "sgen-gray.c"
#include "sgen-workers.c"
-#include "sgen-los.c"
#include "sgen-cardtable.c"
/* Root bitmap descriptors are simpler: the lower three bits describe the type
static void
scan_object_for_specific_ref (char *start, MonoObject *key)
{
+ char *forwarded;
+
+ if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
+ start = forwarded;
+
#include "sgen-scan-object.h"
}
void
-mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data)
+mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
{
while (start < end) {
size_t size;
+ char *obj;
+
if (!*(void**)start) {
start += sizeof (void*); /* should be ALLOC_ALIGN, really */
continue;
}
- size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
+ if (allow_flags) {
+ if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
+ obj = start;
+ } else {
+ obj = start;
+ }
- callback (start, size, data);
+ size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
+
+ callback (obj, size, data);
start += size;
}
void
mono_gc_scan_for_specific_ref (MonoObject *key)
{
- LOSObject *bigobj;
RootRecord *root;
int i;
mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
- (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
+ (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
- major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
+ major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
- for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
- scan_object_for_specific_ref (bigobj->data, key);
+ mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
}
}
+static void
+clear_current_nursery_fragment (char *next)
+{
+ if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
+ g_assert (next <= nursery_frag_real_end);
+ DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", next, nursery_frag_real_end));
+ memset (next, 0, nursery_frag_real_end - next);
+ }
+}
+
/* Clear all remaining nursery fragments */
static void
clear_nursery_fragments (char *next)
{
Fragment *frag;
if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
- g_assert (next <= nursery_frag_real_end);
- memset (next, 0, nursery_frag_real_end - next);
+ clear_current_nursery_fragment (next);
for (frag = nursery_fragments; frag; frag = frag->next) {
+ DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_start, frag->fragment_end));
memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
}
}
LOSObject *bigobj;
mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
- (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
+ (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
- major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
+ major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
{
if (need_remove_object_for_domain (obj, domain))
- major.free_non_pinned_object (obj, size);
+ major_collector.free_non_pinned_object (obj, size);
}
static void
clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
{
if (need_remove_object_for_domain (obj, domain))
- major.free_pinned_object (obj, size);
+ major_collector.free_pinned_object (obj, size);
}
/*
}
mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
- (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain);
+ (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
/*Ephemerons and dislinks must be processed before LOS since they might end up pointing
to memory returned to the OS.*/
objects with major-mark&sweep), but we might need to
dereference a pointer from an object to another object if
the first object is a proxy. */
- major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
+ major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
clear_domain_process_object (bigobj->data, domain);
bigobj = bigobj->next;
DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
bigobj->data));
- free_large_object (to_free);
+ mono_sgen_los_free_object (to_free);
continue;
}
prev = bigobj;
bigobj = bigobj->next;
}
- major.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
- major.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
+ major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
+ major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
UNLOCK_GC;
}
g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
- lock = (current_collection_generation == GENERATION_OLD && major.is_parallel);
+ lock = (current_collection_generation == GENERATION_OLD && major_collector.is_parallel);
if (lock)
LOCK_GLOBAL_REMSET;
* frequently after each object is copied, to achieve better locality and cache
* usage.
*/
-static void
-drain_gray_stack (GrayQueue *queue)
+static gboolean
+drain_gray_stack (GrayQueue *queue, int max_objs)
{
char *obj;
for (;;) {
GRAY_OBJECT_DEQUEUE (queue, obj);
if (!obj)
- break;
+ return TRUE;
DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
- major.minor_scan_object (obj, queue);
+ major_collector.minor_scan_object (obj, queue);
}
} else {
- if (major.is_parallel && queue == &workers_distribute_gray_queue)
- return;
+ int i;
- for (;;) {
- GRAY_OBJECT_DEQUEUE (queue, obj);
- if (!obj)
- break;
- DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
- major.major_scan_object (obj, queue);
- }
+ if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
+ return TRUE;
+
+ do {
+ for (i = 0; i != max_objs; ++i) {
+ GRAY_OBJECT_DEQUEUE (queue, obj);
+ if (!obj)
+ return TRUE;
+ DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
+ major_collector.major_scan_object (obj, queue);
+ }
+ } while (max_objs < 0);
+ return FALSE;
}
}
void *addr;
int idx;
void **definitely_pinned = start;
+ Fragment *frag;
+
+ /*
+ * The code below starts the search from an entry in scan_starts, which might point into a nursery
+ * fragment containing random data. Clearing the nursery fragments takes a lot of time, and searching
+ * though them too, so lay arrays at each location inside a fragment where a search can start:
+ * - scan_locations[i]
+ * - start_nursery
+ * - the start of each fragment (the last_obj + last_obj case)
+ * The third encompasses the first two, since scan_locations [i] can't point inside a nursery fragment.
+ */
+ for (frag = nursery_fragments; frag; frag = frag->next) {
+ MonoArray *o;
+
+ g_assert (frag->fragment_end - frag->fragment_start >= sizeof (MonoArray));
+ o = (MonoArray*)frag->fragment_start;
+ memset (o, 0, sizeof (MonoArray));
+ g_assert (array_fill_vtable);
+ o->obj.vtable = array_fill_vtable;
+ /* Mark this as not a real object */
+ o->obj.synchronisation = GINT_TO_POINTER (-1);
+ o->max_length = (frag->fragment_end - frag->fragment_start) - sizeof (MonoArray);
+ g_assert (frag->fragment_start + safe_object_get_size ((MonoObject*)o) == frag->fragment_end);
+ }
+
while (start < end) {
addr = *start;
/* the range check should be reduntant */
/* now addr should be in an object a short distance from search_start
* Note that search_start must point to zeroed mem or point to an object.
*/
+
do {
if (!*(void**)search_start) {
+ /* Consistency check */
+ /*
+ for (frag = nursery_fragments; frag; frag = frag->next) {
+ if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
+ g_assert_not_reached ();
+ }
+ */
+
search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
continue;
}
last_obj = search_start;
last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
- DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
- if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
- DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
- binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
- pin_object (search_start);
- GRAY_OBJECT_ENQUEUE (queue, search_start);
- if (heap_dump_file)
- mono_sgen_pin_stats_register_object (search_start, last_obj_size);
- definitely_pinned [count] = search_start;
- count++;
- break;
+
+ if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
+ /* Marks the beginning of a nursery fragment, skip */
+ } else {
+ DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
+ if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
+ DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
+ binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
+ pin_object (search_start);
+ GRAY_OBJECT_ENQUEUE (queue, search_start);
+ if (heap_dump_file)
+ mono_sgen_pin_stats_register_object (search_start, last_obj_size);
+ definitely_pinned [count] = search_start;
+ count++;
+ break;
+ }
}
/* skip to the next object */
search_start = (void*)((char*)search_start + last_obj_size);
start++;
}
//printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
+ if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
+ GCRootReport report;
+ report.count = 0;
+ for (idx = 0; idx < count; ++idx)
+ add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
+ notify_gc_roots (&report);
+ }
+ stat_pinned_objects += count;
return count;
}
}
}
+
+void
+mono_sgen_pin_object (void *object, GrayQueue *queue)
+{
+ if (major_collector.is_parallel) {
+ LOCK_PIN_QUEUE;
+ /*object arrives pinned*/
+ pin_stage_ptr (object);
+ ++objects_pinned ;
+ UNLOCK_PIN_QUEUE;
+ } else {
+ SGEN_PIN_OBJECT (object);
+ pin_stage_ptr (object);
+ ++objects_pinned;
+ }
+ GRAY_OBJECT_ENQUEUE (queue, object);
+}
+
/* Sort the addresses in array in increasing order.
* Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
*/
pin_stage_ptr ((void*)addr);
if (heap_dump_file)
pin_stats_register_address ((char*)addr, pin_type);
- DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
+ DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
count++;
}
start++;
if ((desc & 1) && *start_root) {
copy_func (start_root, queue);
DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
- drain_gray_stack (queue);
+ drain_gray_stack (queue, -1);
}
desc >>= 1;
start_root++;
if ((bmap & 1) && *objptr) {
copy_func (objptr, queue);
DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
- drain_gray_stack (queue);
+ drain_gray_stack (queue, -1);
}
bmap >>= 1;
++objptr;
}
}
+static void
+reset_heap_boundaries (void)
+{
+ lowest_heap_address = ~(mword)0;
+ highest_heap_address = 0;
+}
+
void
mono_sgen_update_heap_boundaries (mword low, mword high)
{
g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
alloc_size = nursery_size;
#ifdef SGEN_ALIGN_NURSERY
- data = major.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
+ data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
#else
- data = major.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
+ data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
#endif
nursery_start = data;
nursery_real_end = nursery_start + nursery_size;
return nursery_start;
}
+gboolean
+mono_gc_precise_stack_mark_enabled (void)
+{
+ return !conservative_stack_mark;
+}
+
+FILE *
+mono_gc_get_logfile (void)
+{
+ return mono_sgen_get_logfile ();
+}
+
+static void
+report_finalizer_roots_list (FinalizeEntry *list)
+{
+ GCRootReport report;
+ FinalizeEntry *fin;
+
+ report.count = 0;
+ for (fin = list; fin; fin = fin->next) {
+ if (!fin->object)
+ continue;
+ add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
+ }
+ notify_gc_roots (&report);
+}
+
+static void
+report_finalizer_roots (void)
+{
+ report_finalizer_roots_list (fin_ready_list);
+ report_finalizer_roots_list (critical_fin_list);
+}
+
+static GCRootReport *root_report;
+
+static void
+single_arg_report_root (void **obj)
+{
+ if (*obj)
+ add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
+}
+
+static void
+precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
+{
+ switch (desc & ROOT_DESC_TYPE_MASK) {
+ case ROOT_DESC_BITMAP:
+ desc >>= ROOT_DESC_TYPE_SHIFT;
+ while (desc) {
+ if ((desc & 1) && *start_root) {
+ add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
+ }
+ desc >>= 1;
+ start_root++;
+ }
+ return;
+ case ROOT_DESC_COMPLEX: {
+ gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
+ int bwords = (*bitmap_data) - 1;
+ void **start_run = start_root;
+ bitmap_data++;
+ while (bwords-- > 0) {
+ gsize bmap = *bitmap_data++;
+ void **objptr = start_run;
+ while (bmap) {
+ if ((bmap & 1) && *objptr) {
+ add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
+ }
+ bmap >>= 1;
+ ++objptr;
+ }
+ start_run += GC_BITS_PER_WORD;
+ }
+ break;
+ }
+ case ROOT_DESC_USER: {
+ MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
+ root_report = report;
+ marker (start_root, single_arg_report_root);
+ break;
+ }
+ case ROOT_DESC_RUN_LEN:
+ g_assert_not_reached ();
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+report_registered_roots_by_type (int root_type)
+{
+ GCRootReport report;
+ int i;
+ RootRecord *root;
+ report.count = 0;
+ for (i = 0; i < roots_hash_size [root_type]; ++i) {
+ for (root = roots_hash [root_type][i]; root; root = root->next) {
+ DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
+ precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
+ }
+ }
+ notify_gc_roots (&report);
+}
+
+static void
+report_registered_roots (void)
+{
+ report_registered_roots_by_type (ROOT_TYPE_NORMAL);
+ report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
+}
+
static void
scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
{
Fragment *fragment;
DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
binary_protocol_empty (frag_start, frag_size);
- /* memsetting just the first chunk start is bound to provide better cache locality */
- if (nursery_clear_policy == CLEAR_AT_GC)
- memset (frag_start, 0, frag_size);
/* Not worth dealing with smaller fragments: need to tune */
if (frag_size >= FRAGMENT_MIN_SIZE) {
+ /* memsetting just the first chunk start is bound to provide better cache locality */
+ if (nursery_clear_policy == CLEAR_AT_GC)
+ memset (frag_start, 0, frag_size);
+
fragment = alloc_fragment ();
fragment->fragment_start = frag_start;
fragment->fragment_limit = frag_start;
}
}
+static MonoObject **finalized_array = NULL;
+static int finalized_array_capacity = 0;
+static int finalized_array_entries = 0;
+
+static void
+bridge_register_finalized_object (MonoObject *object)
+{
+ if (!finalized_array)
+ return;
+
+ if (finalized_array_entries >= finalized_array_capacity) {
+ MonoObject **new_array;
+ g_assert (finalized_array_entries == finalized_array_capacity);
+ finalized_array_capacity *= 2;
+ new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
+ memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
+ mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
+ finalized_array = new_array;
+ }
+ finalized_array [finalized_array_entries++] = object;
+}
+
static void
finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
{
TV_DECLARE (btv);
int fin_ready;
int ephemeron_rounds = 0;
- CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major.copy_object : major.copy_or_mark_object;
+ int num_loops;
+ CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
/*
* We copied all the reachable objects. Now it's the time to copy
* To achieve better cache locality and cache usage, we drain the gray stack
* frequently, after each object is copied, and just finish the work here.
*/
- drain_gray_stack (queue);
+ drain_gray_stack (queue, -1);
TV_GETTIME (atv);
DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
+
+ /*
+ We must clear weak links that don't track resurrection before processing object ready for
+ finalization so they can be cleared before that.
+ */
+ null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
+ if (generation == GENERATION_OLD)
+ null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
+
+ if (finalized_array == NULL && mono_sgen_need_bridge_processing ()) {
+ finalized_array_capacity = 32;
+ finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
+ }
+ finalized_array_entries = 0;
+
/* walk the finalization queue and move also the objects that need to be
* finalized: use the finalized objects as new roots so the objects they depend
* on are also not reclaimed. As with the roots above, only objects in the nursery
* We need a loop here, since objects ready for finalizers may reference other objects
* that are fin-ready. Speedup with a flag?
*/
+ num_loops = 0;
do {
/*
* Walk the ephemeron tables marking all values with reachable keys. This must be completely done
int done_with_ephemerons = 0;
do {
done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
- drain_gray_stack (queue);
+ drain_gray_stack (queue, -1);
++ephemeron_rounds;
} while (!done_with_ephemerons);
if (generation == GENERATION_OLD)
finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
+ if (fin_ready != num_ready_finalizers) {
+ ++num_loops;
+ if (finalized_array != NULL)
+ mono_sgen_bridge_processing (finalized_array_entries, finalized_array);
+ }
+
/* drain the new stack that might have been created */
DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
- drain_gray_stack (queue);
+ drain_gray_stack (queue, -1);
} while (fin_ready != num_ready_finalizers);
+ if (mono_sgen_need_bridge_processing ())
+ g_assert (num_loops <= 1);
+
/*
* Clear ephemeron pairs with unreachable keys.
* We pass the copy func so we can figure out if an array was promoted or not.
*/
g_assert (gray_object_queue_is_empty (queue));
for (;;) {
- null_link_in_range (copy_func, start_addr, end_addr, generation, queue);
+ null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
if (generation == GENERATION_OLD)
- null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, queue);
+ null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
if (gray_object_queue_is_empty (queue))
break;
- drain_gray_stack (queue);
+ drain_gray_stack (queue, -1);
}
g_assert (gray_object_queue_is_empty (queue));
if (!do_scan_starts_check)
return;
mono_sgen_check_section_scan_starts (nursery_section);
- major.check_scan_starts ();
+ major_collector.check_scan_starts ();
}
static int last_num_pinned = 0;
mono_sgen_dump_section (nursery_section, "nursery");
- major.dump_heap (heap_dump_file);
+ major_collector.dump_heap (heap_dump_file);
fprintf (heap_dump_file, "<los>\n");
for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
/* FIXME: handle this for parallel collector */
- g_assert (!major.is_parallel);
+ g_assert (!major_collector.is_parallel);
if (moved_objects_idx == MOVED_OBJECTS_NUM) {
mono_profiler_gc_moves (moved_objects, moved_objects_idx);
mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
+ mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
+ mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
#ifdef HEAVY_STATISTICS
mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
inited = TRUE;
}
+static gboolean need_calculate_minor_collection_allowance;
+
+static int last_collection_old_num_major_sections;
+static mword last_collection_los_memory_usage = 0;
+static mword last_collection_old_los_memory_usage;
+static mword last_collection_los_memory_alloced;
+
+static void
+reset_minor_collection_allowance (void)
+{
+ need_calculate_minor_collection_allowance = TRUE;
+}
+
+static void
+try_calculate_minor_collection_allowance (gboolean overwrite)
+{
+ int num_major_sections, num_major_sections_saved, save_target, allowance_target;
+ mword los_memory_saved;
+
+ if (overwrite)
+ g_assert (need_calculate_minor_collection_allowance);
+
+ if (!need_calculate_minor_collection_allowance)
+ return;
+
+ if (!*major_collector.have_swept) {
+ if (overwrite)
+ minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
+ return;
+ }
+
+ num_major_sections = major_collector.get_num_major_sections ();
+
+ num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
+ los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
+
+ save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 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 = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major_collector.section_size + last_collection_los_memory_alloced) / (double)(num_major_sections_saved * major_collector.section_size + los_memory_saved));
+
+ minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
+
+ if (major_collector.have_computed_minor_collection_allowance)
+ major_collector.have_computed_minor_collection_allowance ();
+
+ need_calculate_minor_collection_allowance = FALSE;
+}
+
static gboolean
-need_major_collection (void)
+need_major_collection (mword space_needed)
{
- mword los_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
- return minor_collection_sections_alloced * major.section_size + los_alloced > minor_collection_allowance;
+ mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
+ return (space_needed > available_free_space ()) ||
+ minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
+}
+
+gboolean
+mono_sgen_need_major_collection (mword space_needed)
+{
+ return need_major_collection (space_needed);
}
/*
static gboolean
collect_nursery (size_t requested_size)
{
+ gboolean needs_major;
size_t max_garbage_amount;
char *orig_nursery_next;
TV_DECLARE (all_atv);
TV_DECLARE (atv);
TV_DECLARE (btv);
+ mono_perfcounters->gc_collections0++;
+
current_collection_generation = GENERATION_NURSERY;
binary_protocol_collection (GENERATION_NURSERY);
check_scan_starts ();
degraded_mode = 0;
+ objects_pinned = 0;
orig_nursery_next = nursery_next;
nursery_next = MAX (nursery_next, nursery_last_pinned_end);
/* FIXME: optimize later to use the higher address where an object can be present */
/* world must be stopped already */
TV_GETTIME (all_atv);
- TV_GETTIME (atv);
+ atv = all_atv;
- /* Pinning depends on this */
- clear_nursery_fragments (orig_nursery_next);
+ /* Pinning no longer depends on clearing all nursery fragments */
+ clear_current_nursery_fragment (orig_nursery_next);
TV_GETTIME (btv);
time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
nursery_section->next_data = nursery_next;
- major.start_nursery_collection ();
+ major_collector.start_nursery_collection ();
+
+ try_calculate_minor_collection_allowance (FALSE);
gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
/* pin from pinned handles */
init_pinning ();
+ mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
pin_from_roots (nursery_start, nursery_next);
/* identify pinned objects */
optimize_pin_queue (0);
DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
if (use_cardtable) {
+ atv = btv;
+ card_tables_collect_stats (TRUE);
scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
- //collect_faulted_cards ();
+ TV_GETTIME (btv);
+ time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
}
- drain_gray_stack (&gray_queue);
+ drain_gray_stack (&gray_queue, -1);
+ if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+ report_registered_roots ();
+ if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+ report_finalizer_roots ();
TV_GETTIME (atv);
time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
/* registered roots, this includes static fields */
- scan_from_registered_roots (major.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
- scan_from_registered_roots (major.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
+ scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
+ scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
TV_GETTIME (btv);
time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
/* thread data */
finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
TV_GETTIME (atv);
time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
+ mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
+
+ if (objects_pinned) {
+ evacuate_pin_staging_area ();
+ optimize_pin_queue (0);
+ nursery_section->pin_queue_start = pin_queue;
+ nursery_section->pin_queue_num_entries = next_pin_slot;
+ }
/* 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.
*/
+ mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
build_nursery_fragments (pin_queue, next_pin_slot);
+ mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
TV_GETTIME (btv);
time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
if (consistency_check_at_minor_collection)
check_major_refs ();
- major.finish_nursery_collection ();
+ major_collector.finish_nursery_collection ();
TV_GETTIME (all_btv);
mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
g_assert (gray_object_queue_is_empty (&gray_queue));
+ if (use_cardtable)
+ card_tables_collect_stats (FALSE);
+
check_scan_starts ();
binary_protocol_flush_buffers (FALSE);
+ /*objects are late pinned because of lack of memory, so a major is a good call*/
+ needs_major = need_major_collection (0) || objects_pinned;
current_collection_generation = -1;
+ objects_pinned = 0;
- return need_major_collection ();
+ return needs_major;
}
static void
*/
char *heap_start = NULL;
char *heap_end = (char*)-1;
- int old_num_major_sections = major.get_num_major_sections ();
- int num_major_sections, num_major_sections_saved, save_target, allowance_target;
- mword los_memory_saved, los_memory_alloced, old_los_memory_usage;
+ int old_next_pin_slot;
+
+ mono_perfcounters->gc_collections1++;
+
+ last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
/*
* A domain could have been freed, resulting in
- * los_memory_usage being less than last_los_memory_usage.
+ * los_memory_usage being less than last_collection_los_memory_usage.
*/
- los_memory_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
- old_los_memory_usage = los_memory_usage;
+ last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
+ last_collection_old_los_memory_usage = los_memory_usage;
+ objects_pinned = 0;
//count_ref_nonref_objs ();
//consistency_check ();
binary_protocol_collection (GENERATION_OLD);
check_scan_starts ();
gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
- if (major.is_parallel)
- gray_object_queue_init (&workers_distribute_gray_queue, mono_sgen_get_unmanaged_allocator ());
+ workers_init_distribute_gray_queue ();
degraded_mode = 0;
DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
/* world must be stopped already */
TV_GETTIME (all_atv);
- TV_GETTIME (atv);
+ atv = all_atv;
/* 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.
*/
+
+ if (major_collector.start_major_collection)
+ major_collector.start_major_collection ();
+
+ *major_collector.have_swept = FALSE;
+ reset_minor_collection_allowance ();
+
+ if (xdomain_checks)
+ check_for_xdomain_refs ();
+
/* The remsets are not useful for a major collection */
clear_remsets ();
global_remset_cache_clear ();
DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
/* first pass for the sections */
mono_sgen_find_section_pin_queue_start_end (nursery_section);
- major.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
/* 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) {
}
/* second pass for the sections */
mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- major.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ old_next_pin_slot = next_pin_slot;
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));
- major.init_to_space ();
+ major_collector.init_to_space ();
+
+#ifdef SGEN_DEBUG_INTERNAL_ALLOC
+ main_gc_thread = pthread_self ();
+#endif
- workers_start_all_workers (1);
+ workers_start_all_workers ();
+ workers_start_marking ();
+ if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+ report_registered_roots ();
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, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- scan_from_registered_roots (major.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
TV_GETTIME (btv);
time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
TV_GETTIME (btv);
time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
+ if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
+ report_finalizer_roots ();
/* scan the list of objects ready for finalization */
- scan_finalizer_entries (major.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
- scan_finalizer_entries (major.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
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);
- if (major.is_parallel) {
+ if (major_collector.is_parallel) {
while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
workers_distribute_gray_queue_sections ();
- usleep (2000);
+ usleep (1000);
}
}
- workers_change_num_working (-1);
workers_join ();
- if (major.is_parallel)
+#ifdef SGEN_DEBUG_INTERNAL_ALLOC
+ main_gc_thread = NULL;
+#endif
+
+ if (major_collector.is_parallel)
g_assert (gray_object_queue_is_empty (&gray_queue));
/* all the objects in the heap */
TV_GETTIME (atv);
time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
+ /*
+ * The (single-threaded) finalization code might have done
+ * some copying/marking so we can only reset the GC thread's
+ * worker data here instead of earlier when we joined the
+ * workers.
+ */
+ if (major_collector.reset_worker_data)
+ major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
+
+ if (objects_pinned) {
+ /*This is slow, but we just OOM'd*/
+ mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
+ evacuate_pin_staging_area ();
+ optimize_pin_queue (0);
+ mono_sgen_find_section_pin_queue_start_end (nursery_section);
+ objects_pinned = 0;
+ }
+
+ reset_heap_boundaries ();
+ mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
+
/* sweep the big objects list */
prevbo = NULL;
for (bigobj = los_object_list; bigobj;) {
if (object_is_pinned (bigobj->data)) {
unpin_object (bigobj->data);
+ mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
} else {
LOSObject *to_free;
/* not referenced anywhere, so we can free it */
los_object_list = bigobj->next;
to_free = bigobj;
bigobj = bigobj->next;
- free_large_object (to_free);
+ mono_sgen_los_free_object (to_free);
continue;
}
prevbo = bigobj;
TV_GETTIME (btv);
time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
- los_sweep ();
+ mono_sgen_los_sweep ();
TV_GETTIME (atv);
time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
- major.sweep ();
+ major_collector.sweep ();
TV_GETTIME (btv);
time_major_sweep += TV_ELAPSED_MS (atv, btv);
g_assert (gray_object_queue_is_empty (&gray_queue));
- num_major_sections = major.get_num_major_sections ();
-
- num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 0);
- los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
-
- save_target = ((num_major_sections * major.section_size) + los_memory_saved) / 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 = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major.section_size + los_memory_alloced) / (double)(num_major_sections_saved * major.section_size + los_memory_saved));
-
- minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
+ try_calculate_minor_collection_allowance (TRUE);
minor_collection_sections_alloced = 0;
- last_los_memory_usage = los_memory_usage;
+ last_collection_los_memory_usage = los_memory_usage;
- major.finish_major_collection ();
+ major_collector.finish_major_collection ();
check_scan_starts ();
current_collection_generation = -1;
}
+void
+sgen_collect_major_no_lock (const char *reason)
+{
+ mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
+ stop_world (1);
+ major_collection (reason);
+ restart_world (1);
+ mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
+}
+
/*
* When deciding if it's better to collect or to expand, keep track
* of how much garbage was reclaimed with the last collection: if it's too
g_assert (nursery_section);
if (do_minor_collection) {
- stop_world ();
- if (collect_nursery (size))
+ mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
+ stop_world (0);
+ if (collect_nursery (size)) {
+ mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
major_collection ("minor overflow");
+ /* keep events symmetric */
+ mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
+ }
DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
- restart_world ();
+ restart_world (0);
/* this also sets the proper pointers for the next allocation */
if (!search_fragment_for_size (size)) {
int i;
}
degraded_mode = 1;
}
+ mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
}
//report_internal_mem_usage ();
}
printf ("Internal memory usage:\n");
mono_sgen_report_internal_mem_usage ();
printf ("Pinned memory usage:\n");
- major.report_pinned_memory_usage ();
+ major_collector.report_pinned_memory_usage ();
}
/*
Fragment *frag, *prev;
DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
- if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
+ if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
/* Clear the remaining space, pinning depends on this */
memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
+ }
prev = NULL;
for (frag = nursery_fragments; frag; frag = frag->next) {
Fragment *frag, *prev, *min_prev;
DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, desired size: %zd minimum size %zd\n", nursery_frag_real_end, desired_size, minimum_size));
- if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
+ if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
/* Clear the remaining space, pinning depends on this */
memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
+ }
min_prev = GINT_TO_POINTER (-1);
prev = NULL;
static void*
alloc_degraded (MonoVTable *vtable, size_t size)
{
- if (need_major_collection ()) {
- stop_world ();
+ if (need_major_collection (0)) {
+ mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
+ stop_world (1);
major_collection ("degraded overflow");
- restart_world ();
+ restart_world (1);
+ mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
}
- degraded_mode += size;
- return major.alloc_degraded (vtable, size);
+ return major_collector.alloc_degraded (vtable, size);
}
/*
g_assert (vtable->gc_descr);
if (G_UNLIKELY (collect_before_allocs)) {
- if (nursery_section) {
- stop_world ();
+ static int alloc_count;
+
+ InterlockedIncrement (&alloc_count);
+ if (((alloc_count % collect_before_allocs) == 0) && nursery_section) {
+ mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
+ stop_world (0);
collect_nursery (0);
- restart_world ();
- if (!degraded_mode && !search_fragment_for_size (size)) {
+ restart_world (0);
+ mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
+ if (!degraded_mode && !search_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
// FIXME:
g_assert_not_reached ();
}
*/
if (size > MAX_SMALL_OBJ_SIZE) {
- p = alloc_large_inner (vtable, size);
+ p = mono_sgen_los_alloc_large_inner (vtable, size);
} else {
/* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
g_assert (0);
}
- if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
+ if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
memset (p, 0, size);
+ }
} else {
int alloc_size = tlab_size;
int available_in_nursery = nursery_frag_real_end - nursery_next;
TLAB_REAL_END = TLAB_START + alloc_size;
TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
- if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
+ if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
memset (TLAB_START, 0, alloc_size);
+ }
/* Allocate from the TLAB */
p = (void*)TLAB_NEXT;
}
}
- DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
- binary_protocol_alloc (p, vtable, size);
- *p = vtable;
+ if (G_LIKELY (p)) {
+ DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
+ binary_protocol_alloc (p, vtable, size);
+ *p = vtable;
+ }
return p;
}
LOCK_GC;
res = mono_gc_alloc_obj_nolock (vtable, size);
UNLOCK_GC;
+ if (G_UNLIKELY (!res))
+ return mono_gc_out_of_memory (size);
return res;
}
LOCK_GC;
arr = mono_gc_alloc_obj_nolock (vtable, size);
+ if (G_UNLIKELY (!arr)) {
+ UNLOCK_GC;
+ return mono_gc_out_of_memory (size);
+ }
+
arr->max_length = max_length;
UNLOCK_GC;
LOCK_GC;
arr = mono_gc_alloc_obj_nolock (vtable, size);
+ if (G_UNLIKELY (!arr)) {
+ UNLOCK_GC;
+ return mono_gc_out_of_memory (size);
+ }
+
arr->max_length = max_length;
bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
LOCK_GC;
str = mono_gc_alloc_obj_nolock (vtable, size);
+ if (G_UNLIKELY (!str)) {
+ UNLOCK_GC;
+ return mono_gc_out_of_memory (size);
+ }
+
str->length = len;
UNLOCK_GC;
void*
mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
{
- /* FIXME: handle OOM */
void **p;
size = ALIGN_UP (size);
LOCK_GC;
+
if (size > MAX_SMALL_OBJ_SIZE) {
/* large objects are always pinned anyway */
- p = alloc_large_inner (vtable, size);
+ p = mono_sgen_los_alloc_large_inner (vtable, size);
} else {
DEBUG (9, g_assert (vtable->klass->inited));
- p = major.alloc_small_pinned_obj (size, vtable->klass->has_references);
+ p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
+ }
+ if (G_LIKELY (p)) {
+ DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
+ binary_protocol_alloc_pinned (p, vtable, size);
+ *p = vtable;
}
- DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
- binary_protocol_alloc_pinned (p, vtable, size);
- *p = vtable;
UNLOCK_GC;
return p;
}
+void*
+mono_gc_alloc_mature (MonoVTable *vtable)
+{
+ void **res;
+ size_t size = ALIGN_UP (vtable->klass->instance_size);
+ LOCK_GC;
+ res = alloc_degraded (vtable, size);
+ *res = vtable;
+ UNLOCK_GC;
+ return res;
+}
+
/*
* ######################################################################
* ######## Finalization support
for (i = 0; i < finalizable_hash_size; ++i) {
prev = NULL;
for (entry = finalizable_hash [i]; entry;) {
- if ((char*)entry->object >= start && (char*)entry->object < end && !major.is_object_live (entry->object)) {
+ if ((char*)entry->object >= start && (char*)entry->object < end && !major_collector.is_object_live (entry->object)) {
gboolean is_fin_ready = object_is_fin_ready (entry->object);
char *copy = entry->object;
copy_func ((void**)©, queue);
num_ready_finalizers++;
hash_table->num_registered--;
queue_finalization_entry (entry);
+ bridge_register_finalized_object ((MonoObject*)copy);
/* Make it survive */
from = entry->object;
entry->object = copy;
/*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
if (object < start || object >= end)
return TRUE;
- return !object_is_fin_ready (object) || major.is_object_live (object);
+ return !object_is_fin_ready (object) || major_collector.is_object_live (object);
+}
+
+gboolean
+mono_sgen_object_is_live (void *obj)
+{
+ if (ptr_in_nursery (obj))
+ return object_is_pinned (obj);
+ if (current_collection_generation == GENERATION_NURSERY)
+ return FALSE;
+ return major_collector.is_object_live (obj);
}
/* LOCKING: requires that the GC lock is held */
/* LOCKING: requires that the GC lock is held */
static void
-null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
+null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
{
DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
DisappearingLink **disappearing_link_hash = hash->table;
for (i = 0; i < disappearing_link_hash_size; ++i) {
prev = NULL;
for (entry = disappearing_link_hash [i]; entry;) {
- char *object = DISLINK_OBJECT (entry);
- if (object >= start && object < end && !major.is_object_live (object)) {
- gboolean track = DISLINK_TRACK (entry);
- if (!track && object_is_fin_ready (object)) {
+ char *object;
+ gboolean track = DISLINK_TRACK (entry);
+
+ /*
+ * Tracked references are processed after
+ * finalization handling whereas standard weak
+ * references are processed before. If an
+ * object is still not marked after finalization
+ * handling it means that it either doesn't have
+ * a finalizer or the finalizer has already run,
+ * so we must null a tracking reference.
+ */
+ if (track == before_finalization) {
+ prev = entry;
+ entry = entry->next;
+ continue;
+ }
+
+ object = DISLINK_OBJECT (entry);
+
+ if (object >= start && object < end && !major_collector.is_object_live (object)) {
+ if (object_is_fin_ready (object)) {
void **p = entry->link;
DisappearingLink *old;
*p = NULL;
continue;
} else {
- /* We set the track resurrection bit to
- * FALSE if the object is to be finalized
- * so that the object can be collected in
- * the next cycle (i.e. after it was
- * finalized).
- */
- *entry->link = HIDE_POINTER (copy,
- object_is_fin_ready (object) ? FALSE : track);
+ *entry->link = HIDE_POINTER (copy, track);
DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
}
}
/* FIXME: handle large/small config */
#define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
-static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
+SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
#if USE_SIGNAL_BASED_START_STOP_WORLD
static unsigned int global_stop_count = 0;
static sigset_t suspend_signal_mask;
+
+#ifdef USE_MONO_CTX
+static MonoContext cur_thread_ctx = {0};
+#else
static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
+#endif
-/* LOCKING: assumes the GC lock is held */
-SgenThreadInfo**
-mono_sgen_get_thread_table (void)
-{
- return thread_table;
-}
SgenThreadInfo*
mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
static void
update_current_thread_stack (void *start)
{
+ int stack_guard = 0;
+#ifdef USE_MONO_CTX
+ void *ptr = NULL;
+#else
void *ptr = cur_thread_regs;
+#endif
SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
- info->stack_start = align_pointer (&ptr);
+ info->stack_start = align_pointer (&stack_guard);
g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
+#ifdef USE_MONO_CTX
+ MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
+ info->monoctx = &cur_thread_ctx;
+#else
ARCH_STORE_REGS (ptr);
info->stopped_regs = ptr;
+#endif
if (gc_callbacks.thread_suspend_func)
gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
}
restart_threads_until_none_in_managed_allocator (void)
{
SgenThreadInfo *info;
- int i, result, num_threads_died = 0;
+ int result, num_threads_died = 0;
int sleep_duration = -1;
for (;;) {
int restart_count = 0, restarted_count = 0;
/* restart all threads that stopped in the
allocator */
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- if (info->skip)
- continue;
- if (!info->stack_start || info->in_critical_region ||
- is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
- binary_protocol_thread_restart ((gpointer)info->id);
+ FOREACH_THREAD (info) {
+ if (info->skip)
+ continue;
+ if (!info->stack_start || info->in_critical_region ||
+ is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
+ binary_protocol_thread_restart ((gpointer)info->id);
#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
- result = thread_resume (pthread_mach_thread_np (info->id));
+ result = thread_resume (pthread_mach_thread_np (info->id));
#else
- result = pthread_kill (info->id, restart_signal_num);
+ result = pthread_kill (info->id, restart_signal_num);
#endif
- if (result == 0) {
- ++restart_count;
- } else {
- info->skip = 1;
- }
+ if (result == 0) {
+ ++restart_count;
} else {
- /* we set the stopped_ip to
- NULL for threads which
- we're not restarting so
- that we can easily identify
- the others */
- info->stopped_ip = NULL;
- info->stopped_domain = NULL;
+ info->skip = 1;
}
+ } else {
+ /* we set the stopped_ip to
+ NULL for threads which
+ we're not restarting so
+ that we can easily identify
+ the others */
+ info->stopped_ip = NULL;
+ info->stopped_domain = NULL;
}
- }
+ } END_FOREACH_THREAD
/* if no threads were restarted, we're done */
if (restart_count == 0)
break;
}
/* stop them again */
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- if (info->skip || info->stopped_ip == NULL)
- continue;
+ FOREACH_THREAD (info) {
+ if (info->skip || info->stopped_ip == NULL)
+ continue;
#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
- result = thread_suspend (pthread_mach_thread_np (info->id));
+ result = thread_suspend (pthread_mach_thread_np (info->id));
#else
- result = pthread_kill (info->id, suspend_signal_num);
+ result = pthread_kill (info->id, suspend_signal_num);
#endif
- if (result == 0) {
- ++restarted_count;
- } else {
- info->skip = 1;
- }
+ if (result == 0) {
+ ++restarted_count;
+ } else {
+ info->skip = 1;
}
- }
+ } END_FOREACH_THREAD
/* some threads might have died */
num_threads_died += restart_count - restarted_count;
#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
pthread_t id;
int stop_count;
int old_errno = errno;
+#ifdef USE_MONO_CTX
+ MonoContext monoctx;
+#else
gpointer regs [ARCH_NUM_REGS];
+#endif
gpointer stack_start;
- ucontext_t *ctx = (ucontext_t*)context;
id = pthread_self ();
info = mono_sgen_thread_info_lookup (id);
info->stopped_domain = mono_domain_get ();
- info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (ctx);
+ info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
stop_count = global_stop_count;
/* duplicate signal */
if (0 && info->stop_count == stop_count) {
/* update the remset info in the thread data structure */
info->remset = remembered_set;
#endif
- stack_start = (char*) ARCH_SIGCTX_SP (ctx) - REDZONE_SIZE;
+ stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
/* If stack_start is not within the limits, then don't set it
in info and we will be restarted. */
if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
info->stack_start = stack_start;
- ARCH_COPY_SIGCTX_REGS (regs, ctx);
+#ifdef USE_MONO_CTX
+ mono_sigctx_to_monoctx (context, &monoctx);
+ info->monoctx = &monoctx;
+#else
+ ARCH_COPY_SIGCTX_REGS (regs, context);
info->stopped_regs = regs;
+#endif
} else {
g_assert (!info->stack_start);
}
/* Notify the JIT */
if (gc_callbacks.thread_suspend_func)
- gc_callbacks.thread_suspend_func (info->runtime_data, ctx);
+ gc_callbacks.thread_suspend_func (info->runtime_data, context);
DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
/* notify the waiting thread */
/* LOCKING: assumes the GC lock is held */
static int
-stop_world (void)
+stop_world (int generation)
{
int count;
+ mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
acquire_gc_locks ();
update_current_thread_stack (&count);
count -= restart_threads_until_none_in_managed_allocator ();
g_assert (count >= 0);
DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
+ mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
return count;
}
/* LOCKING: assumes the GC lock is held */
static int
-restart_world (void)
+restart_world (int generation)
{
- int count, i;
+ int count;
SgenThreadInfo *info;
TV_DECLARE (end_sw);
unsigned long usec;
moved_objects_idx = 0;
}
}
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- info->stack_start = NULL;
- info->stopped_regs = NULL;
- }
- }
+ mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
+ FOREACH_THREAD (info) {
+ info->stack_start = NULL;
+#ifdef USE_MONO_CTX
+ info->monoctx = NULL;
+#else
+ info->stopped_regs = NULL;
+#endif
+ } END_FOREACH_THREAD
release_gc_locks ();
usec = TV_ELAPSED (stop_world_time, end_sw);
max_pause_usec = MAX (usec, max_pause_usec);
DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
+ mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
return count;
}
#endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
+int
+mono_sgen_get_current_collection_generation (void)
+{
+ return current_collection_generation;
+}
+
void
mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
{
void*
mono_gc_scan_object (void *obj)
{
- g_assert_not_reached ();
if (current_collection_generation == GENERATION_NURSERY)
- major.copy_object (&obj, &gray_queue);
+ major_collector.copy_object (&obj, &gray_queue);
else
- major.copy_or_mark_object (&obj, &gray_queue);
+ major_collector.copy_or_mark_object (&obj, &gray_queue);
return obj;
}
static void
scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
{
- int i;
SgenThreadInfo *info;
scan_area_arg_start = start_nursery;
scan_area_arg_end = end_nursery;
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- if (info->skip) {
- DEBUG (3, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
- continue;
- }
- DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
- if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
- gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
- else if (!precise)
- conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
-
- if (!precise)
- conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
- start_nursery, end_nursery, PIN_TYPE_STACK);
+ FOREACH_THREAD (info) {
+ if (info->skip) {
+ DEBUG (3, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
+ continue;
}
- }
+ DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
+ if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
+ gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
+ else if (!precise)
+ conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
+
+#ifdef USE_MONO_CTX
+ if (!precise)
+ conservatively_pin_objects_from ((void**)info->monoctx, (void**)info->monoctx + ARCH_NUM_REGS,
+ start_nursery, end_nursery, PIN_TYPE_STACK);
+#else
+ if (!precise)
+ conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
+ start_nursery, end_nursery, PIN_TYPE_STACK);
+#endif
+ } END_FOREACH_THREAD
}
static void
find_pinning_ref_from_thread (char *obj, size_t size)
{
- int i;
+ int j;
SgenThreadInfo *info;
char *endobj = obj + size;
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- char **start = (char**)info->stack_start;
- if (info->skip)
- continue;
- while (start < (char**)info->stack_end) {
- if (*start >= obj && *start < endobj) {
- DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)info->id, start, info->stack_start, info->stack_end));
- }
- start++;
+ FOREACH_THREAD (info) {
+ char **start = (char**)info->stack_start;
+ if (info->skip)
+ continue;
+ while (start < (char**)info->stack_end) {
+ if (*start >= obj && *start < endobj) {
+ DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)info->id, start, info->stack_start, info->stack_end));
}
-
- /* FIXME: check info->stopped_regs */
+ start++;
}
+
+ for (j = 0; j < ARCH_NUM_REGS; ++j) {
+#ifdef USE_MONO_CTX
+ mword w = ((mword*)info->monoctx) [j];
+#else
+ mword w = (mword)info->stopped_regs [j];
+#endif
+
+ if (w >= (mword)obj && w < (mword)obj + size)
+ DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)info->id));
+ } END_FOREACH_THREAD
}
}
//__builtin_prefetch (ptr);
if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
gpointer old = *ptr;
- major.copy_object (ptr, queue);
+ major_collector.copy_object (ptr, queue);
DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
if (old)
binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
return p + 2;
count = p [1];
while (count-- > 0) {
- major.copy_object (ptr, queue);
+ major_collector.copy_object (ptr, queue);
DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
if (!global && *ptr >= start_nursery && *ptr < end_nursery)
mono_sgen_add_to_global_remset (ptr);
ptr = (void**)(*p & ~REMSET_TYPE_MASK);
if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
return p + 1;
- major.minor_scan_object ((char*)ptr, queue);
+ major_collector.minor_scan_object ((char*)ptr, queue);
return p + 1;
case REMSET_VTYPE: {
ptr = (void**)(*p & ~REMSET_TYPE_MASK);
desc = p [1];
count = p [2];
while (count-- > 0)
- ptr = (void**) major.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
+ ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
return p + 3;
}
default:
int i;
mword *addresses, *bumper, *p, *r;
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- for (remset = info->remset; remset; remset = remset->next)
- size += remset->store_next - remset->data;
- }
- }
+ FOREACH_THREAD (info) {
+ for (remset = info->remset; remset; remset = remset->next)
+ size += remset->store_next - remset->data;
+ } END_FOREACH_THREAD
for (remset = freed_thread_remsets; remset; remset = remset->next)
size += remset->store_next - remset->data;
for (remset = global_remset; remset; remset = remset->next)
bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- for (remset = info->remset; remset; remset = remset->next)
- bumper = collect_store_remsets (remset, bumper);
- }
- }
+ FOREACH_THREAD (info) {
+ for (remset = info->remset; remset; remset = remset->next)
+ bumper = collect_store_remsets (remset, bumper);
+ } END_FOREACH_THREAD
for (remset = global_remset; remset; remset = remset->next)
bumper = collect_store_remsets (remset, bumper);
for (remset = freed_thread_remsets; remset; remset = remset->next)
generic_store_remsets = NULL;
/* the per-thread ones */
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- RememberedSet *next;
- int j;
- for (remset = info->remset; remset; remset = next) {
- DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
- for (p = remset->data; p < remset->store_next;)
- p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
- remset->store_next = remset->data;
- next = remset->next;
- remset->next = NULL;
- if (remset != info->remset) {
- DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
- mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
- }
+ FOREACH_THREAD (info) {
+ RememberedSet *next;
+ int j;
+ for (remset = info->remset; remset; remset = next) {
+ DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
+ for (p = remset->data; p < remset->store_next;)
+ p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
+ remset->store_next = remset->data;
+ next = remset->next;
+ remset->next = NULL;
+ if (remset != info->remset) {
+ DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
+ mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
}
- for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
- handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
- clear_thread_store_remset_buffer (info);
}
- }
+ for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
+ handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
+ clear_thread_store_remset_buffer (info);
+ } END_FOREACH_THREAD
/* the freed thread ones */
while (freed_thread_remsets) {
static void
clear_remsets (void)
{
- int i;
SgenThreadInfo *info;
RememberedSet *remset, *next;
generic_store_remsets = gs_next;
}
/* the per-thread ones */
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- for (remset = info->remset; remset; remset = next) {
- remset->store_next = remset->data;
- next = remset->next;
- remset->next = NULL;
- if (remset != info->remset) {
- DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
- mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
- }
+ FOREACH_THREAD (info) {
+ for (remset = info->remset; remset; remset = next) {
+ remset->store_next = remset->data;
+ next = remset->next;
+ remset->next = NULL;
+ if (remset != info->remset) {
+ DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
+ mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
}
- clear_thread_store_remset_buffer (info);
}
- }
+ clear_thread_store_remset_buffer (info);
+ } END_FOREACH_THREAD
/* the freed thread ones */
while (freed_thread_remsets) {
clear_tlabs (void)
{
SgenThreadInfo *info;
- int i;
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- /* A new TLAB will be allocated when the thread does its first allocation */
- *info->tlab_start_addr = NULL;
- *info->tlab_next_addr = NULL;
- *info->tlab_temp_end_addr = NULL;
- *info->tlab_real_end_addr = NULL;
- }
- }
+ FOREACH_THREAD (info) {
+ /* A new TLAB will be allocated when the thread does its first allocation */
+ *info->tlab_start_addr = NULL;
+ *info->tlab_next_addr = NULL;
+ *info->tlab_temp_end_addr = NULL;
+ *info->tlab_real_end_addr = NULL;
+ } END_FOREACH_THREAD
}
/* LOCKING: assumes the GC lock is held */
info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
info->stopped_ip = NULL;
info->stopped_domain = NULL;
+#ifdef USE_MONO_CTX
+ info->monoctx = NULL;
+#else
info->stopped_regs = NULL;
+#endif
binary_protocol_thread_register ((gpointer)info->id);
} else {
prev->next = p->next;
}
+
+ if (gc_callbacks.thread_detach_func) {
+ gc_callbacks.thread_detach_func (p->runtime_data);
+ p->runtime_data = NULL;
+ }
+
if (p->remset) {
if (freed_thread_remsets) {
for (rset = p->remset; rset->next; rset = rset->next)
static void
unregister_thread (void *k)
{
- g_assert (!mono_domain_get ());
+ /* If a delegate is passed to native code and invoked on a thread we dont
+ * know about, the jit will register it with mono_jit_thead_attach, but
+ * we have no way of knowing when that thread goes away. SGen has a TSD
+ * so we assume that if the domain is still registered, we can detach
+ * the thread
+ */
+ if (mono_domain_get ())
+ mono_thread_detach (mono_thread_current ());
+
LOCK_GC;
unregister_current_thread ();
UNLOCK_GC;
LOCK_GC;
init_stats ();
info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
- if (info == NULL)
+ if (info == NULL) {
info = gc_register_current_thread (baseptr);
+ } else {
+ /* The main thread might get registered before callbacks are set */
+ if (gc_callbacks.thread_attach_func && !info->runtime_data)
+ info->runtime_data = gc_callbacks.thread_attach_func ();
+ }
UNLOCK_GC;
+
+ /* Need a better place to initialize this */
+ if (!array_fill_vtable && mono_get_root_domain ()) {
+ array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
+ }
+
return info != NULL;
}
+/*
+ * mono_gc_set_stack_end:
+ *
+ * Set the end of the current threads stack to STACK_END. The stack space between
+ * STACK_END and the real end of the threads stack will not be scanned during collections.
+ */
+void
+mono_gc_set_stack_end (void *stack_end)
+{
+ SgenThreadInfo *info;
+
+ LOCK_GC;
+ info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+ if (info) {
+ g_assert (stack_end < info->stack_end);
+ info->stack_end = stack_end;
+ }
+ UNLOCK_GC;
+}
+
#if USE_PTHREAD_INTERCEPT
typedef struct {
* ######################################################################
*/
+/*
+ * This causes the compile to extend the liveness of 'v' till the call to dummy_use
+ */
+static void
+dummy_use (gpointer v) {
+ __asm__ volatile ("" : "=r"(v) : "r"(v));
+}
+
+
static RememberedSet*
alloc_remset (int size, gpointer id) {
RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
void
mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
{
- RememberedSet *rs;
- TLAB_ACCESS_INIT;
HEAVY_STAT (++stat_wbarrier_set_field);
if (ptr_in_nursery (field_ptr)) {
*(void**)field_ptr = value;
return;
}
DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
- LOCK_GC;
if (use_cardtable) {
- sgen_card_table_mark_address ((mword)field_ptr);
+ *(void**)field_ptr = value;
+ if (ptr_in_nursery (value))
+ sgen_card_table_mark_address ((mword)field_ptr);
+ dummy_use (value);
} else {
+ RememberedSet *rs;
+ TLAB_ACCESS_INIT;
+
+ LOCK_GC;
rs = REMEMBERED_SET;
if (rs->store_next < rs->end_set) {
*(rs->store_next++) = (mword)field_ptr;
mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
#endif
*(rs->store_next++) = (mword)field_ptr;
+ *(void**)field_ptr = value;
+ UNLOCK_GC;
}
- *(void**)field_ptr = value;
- UNLOCK_GC;
}
void
mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
{
- RememberedSet *rs;
- TLAB_ACCESS_INIT;
HEAVY_STAT (++stat_wbarrier_set_arrayref);
if (ptr_in_nursery (slot_ptr)) {
*(void**)slot_ptr = value;
return;
}
DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
- LOCK_GC;
if (use_cardtable) {
- sgen_card_table_mark_address ((mword)slot_ptr);
+ *(void**)slot_ptr = value;
+ if (ptr_in_nursery (value))
+ sgen_card_table_mark_address ((mword)slot_ptr);
+ dummy_use (value);
} else {
+ RememberedSet *rs;
+ TLAB_ACCESS_INIT;
+
+ LOCK_GC;
rs = REMEMBERED_SET;
if (rs->store_next < rs->end_set) {
*(rs->store_next++) = (mword)slot_ptr;
mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
#endif
*(rs->store_next++) = (mword)slot_ptr;
+ *(void**)slot_ptr = value;
+ UNLOCK_GC;
}
- *(void**)slot_ptr = value;
- UNLOCK_GC;
}
void
mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
{
- RememberedSet *rs;
- TLAB_ACCESS_INIT;
HEAVY_STAT (++stat_wbarrier_arrayref_copy);
- LOCK_GC;
- memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
- if (ptr_in_nursery (dest_ptr)) {
- UNLOCK_GC;
+ /*This check can be done without taking a lock since dest_ptr array is pinned*/
+ if (ptr_in_nursery (dest_ptr) || count <= 0) {
+ memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
return;
}
+
if (use_cardtable) {
- sgen_card_table_mark_range ((mword)dest_ptr, count * sizeof (gpointer));
+ gpointer *dest = dest_ptr;
+ gpointer *src = src_ptr;
+
+ /*overlapping that required backward copying*/
+ if (src < dest && (src + count) > dest) {
+ gpointer *start = dest;
+ dest += count - 1;
+ src += count - 1;
+
+ for (; dest >= start; --src, --dest) {
+ gpointer value = *src;
+ *dest = value;
+ if (ptr_in_nursery (value))
+ sgen_card_table_mark_address ((mword)dest);
+ dummy_use (value);
+ }
+ } else {
+ gpointer *end = dest + count;
+ for (; dest < end; ++src, ++dest) {
+ gpointer value = *src;
+ *dest = value;
+ if (ptr_in_nursery (value))
+ sgen_card_table_mark_address ((mword)dest);
+ dummy_use (value);
+ }
+ }
} else {
+ RememberedSet *rs;
+ TLAB_ACCESS_INIT;
+ LOCK_GC;
+ memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
+
rs = REMEMBERED_SET;
DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
if (rs->store_next + 1 < rs->end_set) {
#endif
*(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
*(rs->store_next++) = count;
+
+ UNLOCK_GC;
}
- UNLOCK_GC;
}
static char *found_obj;
static void
-find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
+find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
{
+ char *ptr = user_data;
+
if (ptr >= obj && ptr < obj + size) {
g_assert (!found_obj);
found_obj = obj;
char*
find_object_for_ptr (char *ptr)
{
- LOSObject *bigobj;
-
if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
found_obj = NULL;
mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
- (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
+ find_object_for_ptr_callback, ptr, TRUE);
if (found_obj)
return found_obj;
}
- for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
- if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
- return bigobj->data;
- }
+ found_obj = NULL;
+ mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
+ if (found_obj)
+ return found_obj;
/*
* Very inefficient, but this is debugging code, supposed to
* be called from gdb, so we don't care.
*/
found_obj = NULL;
- major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
+ major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
return found_obj;
}
*(void**)ptr = value;
if (ptr_in_nursery (value))
mono_gc_wbarrier_generic_nostore (ptr);
+ dummy_use (value);
}
void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
sgen_card_table_mark_range ((mword)dest, size);
} else {
rs = REMEMBERED_SET;
- if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !klass->has_references) {
+ if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
UNLOCK_GC;
return;
}
MonoVTable *vtable;
mword desc;
int type;
+ char *start;
if (ptr_in_nursery (ptr)) {
printf ("Pointer inside nursery.\n");
} else {
- if (major.ptr_is_in_non_pinned_space (ptr)) {
+ if (mono_sgen_ptr_is_in_los (ptr, &start)) {
+ if (ptr == start)
+ printf ("Pointer is the start of object %p in LOS space.\n", start);
+ else
+ printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
+ ptr = start;
+ } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
printf ("Pointer inside oldspace.\n");
- } else if (major.obj_is_from_pinned_alloc (ptr)) {
+ } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
printf ("Pointer is inside a pinned chunk.\n");
} else {
printf ("Pointer unknown.\n");
}
/* the per-thread ones */
- for (i = 0; i < THREAD_HASH_SIZE; ++i) {
- for (info = thread_table [i]; info; info = info->next) {
- int j;
- for (remset = info->remset; remset; remset = remset->next) {
- DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
- for (p = remset->data; p < remset->store_next;) {
- p = find_in_remset_loc (p, addr, &found);
- if (found)
- return TRUE;
- }
- }
- for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
- if ((*info->store_remset_buffer_addr) [j + 1] == addr)
+ FOREACH_THREAD (info) {
+ int j;
+ for (remset = info->remset; remset; remset = remset->next) {
+ DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
+ for (p = remset->data; p < remset->store_next;) {
+ p = find_in_remset_loc (p, addr, &found);
+ if (found)
return TRUE;
}
}
- }
+ for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
+ if ((*info->store_remset_buffer_addr) [j + 1] == addr)
+ return TRUE;
+ }
+ } END_FOREACH_THREAD
/* the freed thread ones */
for (remset = freed_thread_remsets; remset; remset = remset->next) {
static void
check_consistency (void)
{
- LOSObject *bigobj;
-
// Need to add more checks
missing_remsets = FALSE;
DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
// Check that oldspace->newspace pointers are registered with the collector
- major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
+ major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
- for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
- check_consistency_callback (bigobj->data, bigobj->size, NULL);
+ mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
-#ifdef SGEN_BINARY_PROTOCOL
- if (!binary_protocol_file)
-#endif
+ if (!binary_protocol_is_enabled ())
g_assert (!missing_remsets);
}
static void
check_major_refs (void)
{
- LOSObject *bigobj;
-
- major.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
-
- for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
- check_major_refs_callback (bigobj->data, bigobj->size, NULL);
+ major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
+ mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
}
/* Check that the reference is valid */
* ######################################################################
*/
+#define REFS_SIZE 128
+typedef struct {
+ void *data;
+ MonoGCReferences callback;
+ int flags;
+ int count;
+ int called;
+ MonoObject *refs [REFS_SIZE];
+ uintptr_t offsets [REFS_SIZE];
+} HeapWalkInfo;
+
+#undef HANDLE_PTR
+#define HANDLE_PTR(ptr,obj) do { \
+ if (*(ptr)) { \
+ if (hwi->count == REFS_SIZE) { \
+ hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
+ hwi->count = 0; \
+ hwi->called = 1; \
+ } \
+ hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
+ hwi->refs [hwi->count++] = *(ptr); \
+ } \
+ } while (0)
+
+static void
+collect_references (HeapWalkInfo *hwi, char *start, size_t size)
+{
+#include "sgen-scan-object.h"
+}
+
+static void
+walk_references (char *start, size_t size, void *data)
+{
+ HeapWalkInfo *hwi = data;
+ hwi->called = 0;
+ hwi->count = 0;
+ collect_references (hwi, start, size);
+ if (hwi->count || !hwi->called)
+ hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
+}
+
+/**
+ * mono_gc_walk_heap:
+ * @flags: flags for future use
+ * @callback: a function pointer called for each object in the heap
+ * @data: a user data pointer that is passed to callback
+ *
+ * This function can be used to iterate over all the live objects in the heap:
+ * for each object, @callback is invoked, providing info about the object's
+ * location in memory, its class, its size and the objects it references.
+ * For each referenced object it's offset from the object address is
+ * reported in the offsets array.
+ * The object references may be buffered, so the callback may be invoked
+ * multiple times for the same object: in all but the first call, the size
+ * argument will be zero.
+ * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
+ * profiler event handler.
+ *
+ * Returns: a non-zero value if the GC doesn't support heap walking
+ */
+int
+mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
+{
+ HeapWalkInfo hwi;
+
+ hwi.flags = flags;
+ hwi.callback = callback;
+ hwi.data = data;
+
+ clear_nursery_fragments (nursery_next);
+ mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
+
+ major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
+ mono_sgen_los_iterate_objects (walk_references, &hwi);
+
+ return 0;
+}
+
void
mono_gc_collect (int generation)
{
LOCK_GC;
- stop_world ();
+ if (generation > 1)
+ generation = 1;
+ mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
+ stop_world (generation);
if (generation == 0) {
collect_nursery (0);
} else {
major_collection ("user request");
}
- restart_world ();
+ restart_world (generation);
+ mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
UNLOCK_GC;
}
LOCK_GC;
tot = los_memory_usage;
tot += nursery_section->next_data - nursery_section->data;
- tot += major.get_used_size ();
+ tot += major_collector.get_used_size ();
/* FIXME: account for pinned objects */
UNLOCK_GC;
return tot;
void*
mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
{
- if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
+ if (numbits == 0) {
+ return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
+ } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
} else {
mword complex = alloc_complex_descriptor (bitmap, numbits);
}
}
+ static void *all_ref_root_descrs [32];
+
+void*
+mono_gc_make_root_descr_all_refs (int numbits)
+{
+ gsize *gc_bitmap;
+ void *descr;
+
+ if (numbits < 32 && all_ref_root_descrs [numbits])
+ return all_ref_root_descrs [numbits];
+
+ gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
+ memset (gc_bitmap, 0xff, numbits / 8);
+ if (numbits % 8)
+ gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
+ descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
+ g_free (gc_bitmap);
+
+ if (numbits < 32)
+ all_ref_root_descrs [numbits] = descr;
+
+ return descr;
+}
+
void*
mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
{
return result;
}
-/* Tries to extract a number from the passed string, taking in to account m, k
- * and g suffixes */
-gboolean
-mono_sgen_parse_environment_string_extract_number (const char *str, glong *out)
-{
- char *endptr;
- int len = strlen (str), shift = 0;
- glong val;
- gboolean is_suffix = FALSE;
- char suffix;
-
- switch (str [len - 1]) {
- case 'g':
- case 'G':
- shift += 10;
- case 'm':
- case 'M':
- shift += 10;
- case 'k':
- case 'K':
- shift += 10;
- is_suffix = TRUE;
- suffix = str [len - 1];
- break;
- }
-
- errno = 0;
- val = strtol (str, &endptr, 10);
-
- if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
- || (errno != 0 && val == 0) || (endptr == str))
- return FALSE;
-
- if (is_suffix) {
- if (*(endptr + 1)) /* Invalid string. */
- return FALSE;
- val <<= shift;
- }
-
- *out = val;
- return TRUE;
-}
-
void
mono_gc_base_init (void)
{
char *env;
char **opts, **ptr;
- char *major_collector = NULL;
+ char *major_collector_opt = NULL;
struct sigaction sinfo;
+ glong max_heap = 0;
+ int num_workers;
+ /* the gc_initialized guard seems to imply this method is
+ idempotent, but LOCK_INIT(gc_mutex) might not be. It's
+ defined in sgen-gc.h as nothing, so there's no danger at
+ present. */
LOCK_INIT (gc_mutex);
LOCK_GC;
if (gc_initialized) {
return;
}
pagesize = mono_pagesize ();
- gc_debug_file = stderr;
+ gc_debug_file = stdout;
LOCK_INIT (interruption_mutex);
LOCK_INIT (global_remset_mutex);
+ LOCK_INIT (pin_queue_mutex);
if ((env = getenv ("MONO_GC_PARAMS"))) {
opts = g_strsplit (env, ",", -1);
char *opt = *ptr;
if (g_str_has_prefix (opt, "major=")) {
opt = strchr (opt, '=') + 1;
- major_collector = g_strdup (opt);
+ major_collector_opt = g_strdup (opt);
}
}
} else {
mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
- if (!major_collector || !strcmp (major_collector, "marksweep")) {
- mono_sgen_marksweep_init (&major);
- } else if (!major_collector || !strcmp (major_collector, "marksweep-fixed")) {
- mono_sgen_marksweep_fixed_init (&major);
- } else if (!major_collector || !strcmp (major_collector, "marksweep-par")) {
- mono_sgen_marksweep_par_init (&major);
- workers_init (mono_cpu_count ());
- } else if (!major_collector || !strcmp (major_collector, "marksweep-fixed-par")) {
- mono_sgen_marksweep_fixed_par_init (&major);
- workers_init (mono_cpu_count ());
- } else if (!strcmp (major_collector, "copying")) {
- mono_sgen_copying_init (&major);
+ if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
+ mono_sgen_marksweep_init (&major_collector);
+ } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
+ mono_sgen_marksweep_fixed_init (&major_collector);
+ } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
+ mono_sgen_marksweep_par_init (&major_collector);
+ } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
+ mono_sgen_marksweep_fixed_par_init (&major_collector);
+ } else if (!strcmp (major_collector_opt, "copying")) {
+ mono_sgen_copying_init (&major_collector);
} else {
- fprintf (stderr, "Unknown major collector `%s'.\n", major_collector);
+ fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
exit (1);
}
#ifdef SGEN_HAVE_CARDTABLE
- use_cardtable = major.supports_cardtable;
+ use_cardtable = major_collector.supports_cardtable;
#else
use_cardtable = FALSE;
#endif
+ num_workers = mono_cpu_count ();
+ g_assert (num_workers > 0);
+ if (num_workers > 16)
+ num_workers = 16;
+
+ /* Keep this the default for now */
+ conservative_stack_mark = TRUE;
+
if (opts) {
for (ptr = opts; *ptr; ++ptr) {
char *opt = *ptr;
use_cardtable = FALSE;
} else if (strcmp (opt, "cardtable") == 0) {
if (!use_cardtable) {
- if (major.supports_cardtable)
+ if (major_collector.supports_cardtable)
fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
else
fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
}
continue;
}
+ if (g_str_has_prefix (opt, "max-heap-size=")) {
+ opt = strchr (opt, '=') + 1;
+ if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
+ if ((max_heap & (mono_pagesize () - 1))) {
+ fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
+ exit (1);
+ }
+ } else {
+ fprintf (stderr, "max-heap-size must be an integer.\n");
+ exit (1);
+ }
+ continue;
+ }
+ if (g_str_has_prefix (opt, "workers=")) {
+ long val;
+ char *endptr;
+ if (!major_collector.is_parallel) {
+ fprintf (stderr, "The workers= option can only be used for parallel collectors.");
+ exit (1);
+ }
+ opt = strchr (opt, '=') + 1;
+ val = strtol (opt, &endptr, 10);
+ if (!*opt || *endptr) {
+ fprintf (stderr, "Cannot parse the workers= option value.");
+ exit (1);
+ }
+ if (val <= 0 || val > 16) {
+ fprintf (stderr, "The number of workers must be in the range 1 to 16.");
+ exit (1);
+ }
+ num_workers = (int)val;
+ continue;
+ }
+ if (g_str_has_prefix (opt, "stack-mark=")) {
+ opt = strchr (opt, '=') + 1;
+ if (!strcmp (opt, "precise")) {
+ conservative_stack_mark = FALSE;
+ } else if (!strcmp (opt, "conservative")) {
+ conservative_stack_mark = TRUE;
+ } else {
+ fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
+ exit (1);
+ }
+ continue;
+ }
#ifdef USER_CONFIG
if (g_str_has_prefix (opt, "nursery-size=")) {
long val;
opt = strchr (opt, '=') + 1;
- if (*opt && mono_sgen_parse_environment_string_extract_number (opt, &val)) {
+ if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
default_nursery_size = val;
#ifdef SGEN_ALIGN_NURSERY
if ((val & (val - 1))) {
continue;
}
#endif
- if (!(major.handle_gc_param && major.handle_gc_param (opt))) {
+ if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
+ fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
- if (major.print_gc_param_usage)
- major.print_gc_param_usage ();
+ fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
+ if (major_collector.print_gc_param_usage)
+ major_collector.print_gc_param_usage ();
exit (1);
}
}
g_strfreev (opts);
}
- if (major_collector)
- g_free (major_collector);
+ if (major_collector.is_parallel)
+ workers_init (num_workers);
+
+ if (major_collector_opt)
+ g_free (major_collector_opt);
nursery_size = DEFAULT_NURSERY_SIZE;
minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
+ init_heap_size_limits (max_heap);
alloc_nursery ();
g_free (rf);
}
} else if (!strcmp (opt, "collect-before-allocs")) {
- collect_before_allocs = TRUE;
+ collect_before_allocs = 1;
+ } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
+ char *arg = strchr (opt, '=') + 1;
+ collect_before_allocs = atoi (arg);
} else if (!strcmp (opt, "check-at-minor-collections")) {
consistency_check_at_minor_collection = TRUE;
nursery_clear_policy = CLEAR_AT_GC;
xdomain_checks = TRUE;
} else if (!strcmp (opt, "clear-at-gc")) {
nursery_clear_policy = CLEAR_AT_GC;
- } else if (!strcmp (opt, "conservative-stack-mark")) {
- conservative_stack_mark = TRUE;
+ } else if (!strcmp (opt, "clear-nursery-at-gc")) {
+ nursery_clear_policy = CLEAR_AT_GC;
} else if (!strcmp (opt, "check-scan-starts")) {
do_scan_starts_check = TRUE;
} else if (g_str_has_prefix (opt, "heap-dump=")) {
#ifdef SGEN_BINARY_PROTOCOL
} else if (g_str_has_prefix (opt, "binary-protocol=")) {
char *filename = strchr (opt, '=') + 1;
- binary_protocol_file = fopen (filename, "w");
+ binary_protocol_init (filename);
#endif
} else {
fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
- fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
+ fprintf (stderr, "Valid options are: collect-before-allocs[=<n>], check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
exit (1);
}
}
g_strfreev (opts);
}
+ if (major_collector.post_param_init)
+ major_collector.post_param_init ();
+
suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
MONO_SEM_INIT (&suspend_ack_semaphore, 0);
mono_mb_emit_i4 ((mb), (offset)); \
} while (0)
#else
+
+/*
+ * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
+ * where the two are the same.
+ */
+#ifdef __APPLE__
#define EMIT_TLS_ACCESS(mb,member,dummy) do { \
mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
mono_mb_emit_byte ((mb), CEE_ADD); \
mono_mb_emit_byte ((mb), CEE_LDIND_I); \
} while (0)
+#else
+#define EMIT_TLS_ACCESS(mb,member,dummy) do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
+#endif
+
#endif
#ifdef MANAGED_ALLOCATION
MonoMethod *method;
int i;
+ if (!mono_thread_internal_current ())
+ /* Happens during thread attach */
+ return FALSE;
+
if (!ip || !domain)
return FALSE;
ji = mono_jit_info_table_find (domain, ip);
return NULL;
if (collect_before_allocs)
return NULL;
- g_assert (!klass->has_finalize && !klass->marshalbyref);
+ g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
#else
va_end (ap);
}
+FILE*
+mono_sgen_get_logfile (void)
+{
+ return gc_debug_file;
+}
+
+#ifdef HOST_WIN32
+BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
+{
+ return TRUE;
+}
+#endif
+
#endif /* HAVE_SGEN_GC */