diff --git a/mono/metadata/domain-internals.h b/mono/metadata/domain-internals.h
index fbfe9d33680a..b9306058bc8f 100644
--- a/mono/metadata/domain-internals.h
+++ b/mono/metadata/domain-internals.h
@@ -315,6 +315,7 @@ struct _MonoDomain {
 	MonoCoopMutex    lock;
 	MonoMemPool        *mp;
 	MonoCodeManager    *code_mp;
+	void               *gc_mp;
 	/*
 	 * keep all the managed objects close to each other for the precise GC
 	 * For the Boehm GC we additionally keep close also other GC-tracked pointers.
diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c
index 227b555833e2..81a097ec4365 100644
--- a/mono/metadata/gc.c
+++ b/mono/metadata/gc.c
@@ -453,7 +453,7 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout)
 
 	/* We don't support domain finalization without a GC */
 	if (mono_gc_is_null ())
-		return FALSE;
+		return TRUE;
 
 	mono_gc_collect (mono_gc_max_generation ());
 
diff --git a/mono/metadata/null-gc-handles.c b/mono/metadata/null-gc-handles.c
index 60e372bd6e98..c19731f57433 100644
--- a/mono/metadata/null-gc-handles.c
+++ b/mono/metadata/null-gc-handles.c
@@ -23,6 +23,10 @@ static mono_mutex_t handle_section;
 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
 
+
+void mono_gc_handle_lock () { lock_handles (NULL); }
+void mono_gc_handle_unlock () { unlock_handles (NULL); }
+
 typedef struct {
 	guint32  *bitmap;
 	gpointer *entries;
@@ -116,7 +120,7 @@ handle_data_alloc_entries (HandleData *handles)
 		handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
 		handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
 	} else {
-		handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "GC Handle Table (Null)");
+		handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Null)");
 	}
 	handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
 }
@@ -183,7 +187,7 @@ handle_data_grow (HandleData *handles, gboolean track)
 		handles->domain_ids = domain_ids;
 	} else {
 		gpointer *entries;
-		entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "GC Handle Table (Null)");
+		entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, NULL, "GC Handle Table (Null)");
 		mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
 		mono_gc_free_fixed (handles->entries);
 		handles->entries = entries;
@@ -224,7 +228,7 @@ alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
 #endif
 	unlock_handles (handles);
 	res = MONO_GC_HANDLE (slot, handles->type);
-	mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
+	MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj));
 	return res;
 }
 
@@ -416,7 +420,7 @@ mono_gchandle_free (guint32 gchandle)
 #endif
 	/*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
 	unlock_handles (handles);
-	mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
+	MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type));
 }
 
 /**
@@ -431,7 +435,7 @@ mono_gchandle_free_domain (MonoDomain *domain)
 {
 	guint type;
 
-	for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
+	for (type = HANDLE_TYPE_MIN; type <= HANDLE_PINNED; ++type) {
 		guint slot;
 		HandleData *handles = &gc_handles [type];
 		lock_handles (handles);
@@ -455,6 +459,31 @@ mono_gchandle_free_domain (MonoDomain *domain)
 	}
 
 }
+
+void
+mono_gc_strong_handle_foreach (GFunc func, gpointer user_data)
+{
+	int gcHandleTypeIndex;
+	uint32_t i;
+
+	lock_handles (handles);
+
+	for (gcHandleTypeIndex = HANDLE_NORMAL; gcHandleTypeIndex <= HANDLE_PINNED; gcHandleTypeIndex++)
+	{
+		HandleData* handles = &gc_handles[gcHandleTypeIndex];
+
+		for (i = 0; i < handles->size; i++)
+		{
+			if (!slot_occupied (handles, i))
+				continue;
+			if (handles->entries[i] != NULL)
+				func (handles->entries[i], user_data);
+		}
+	}
+
+	unlock_handles (handles);
+}
+
 #else
 
 MONO_EMPTY_SOURCE_FILE (null_gc_handles);
diff --git a/mono/metadata/null-gc.c b/mono/metadata/null-gc.c
index 7f91ee38c0ad..9c69322b4df4 100644
--- a/mono/metadata/null-gc.c
+++ b/mono/metadata/null-gc.c
@@ -9,26 +9,116 @@
  */
 
 #include "config.h"
+
+typedef struct _NullGCThreadInfo NullGCThreadInfo;
+#undef THREAD_INFO_TYPE
+#define THREAD_INFO_TYPE NullGCThreadInfo
+
 #include <glib.h>
 #include <mono/metadata/mono-gc.h>
 #include <mono/metadata/gc-internals.h>
 #include <mono/metadata/runtime.h>
 #include <mono/metadata/w32handle.h>
 #include <mono/utils/atomic.h>
+#include <mono/utils/mono-mmap.h>
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/mono-counters.h>
 #include <mono/metadata/null-gc-handles.h>
 
+
+
+struct _NullGCThreadInfo {
+	MonoThreadInfo info;
+
+	/*
+	 * `skip` is set to TRUE when STW fails to suspend a thread, most probably because
+	 * the underlying thread is dead.
+	*/
+	gboolean skip, suspend_done;
+	volatile int in_critical_region;
+
+	/*
+	This is set the argument of mono_gc_set_skip_thread.
+
+	A thread that knowingly holds no managed state can call this
+	function around blocking loops to reduce the GC burden by not
+	been scanned.
+	*/
+	gboolean gc_disabled;
+//
+//#ifdef SGEN_POSIX_STW
+//	/* This is -1 until the first suspend. */
+//	int signal;
+//	/* FIXME: kill this, we only use signals on systems that have rt-posix, which doesn't have issues with duplicates. */
+//	unsigned int stop_count; /* to catch duplicate signals. */
+//#endif
+//
+//	gpointer runtime_data;
+//
+//	void* stack_end;
+//	void* stack_start;
+//	void* stack_start_limit;
+//
+//	MonoContext ctx;		/* ditto */
+};
+
 #ifdef HAVE_NULL_GC
 
 static gboolean gc_inited = FALSE;
 
+struct _GCMemChunk;
+typedef struct _GCMemChunk GCMemChunk;
+
+struct _GCMemChunk
+{
+	GCMemChunk* next;
+	char* start_of_memory;
+	char* current_memory;
+	size_t length;
+};
+
+typedef struct GCMemPool
+{
+	GCMemChunk* chunks;
+	size_t page_size;
+} GCMemPool;
+
+static mono_mutex_t nullgc_mutex;
+
+static GCMemPool* init_gc_mempool ()
+{
+	GCMemPool* mp = g_new0 (GCMemPool, 1);
+	mp->page_size = mono_pagesize ();
+
+	return mp;
+}
+
+static void gc_free_mempool (GCMemPool* mp)
+{
+	if (!mp)
+		return;
+
+	GCMemChunk* chunk = mp->chunks;
+	while (chunk) {
+
+		int res = VirtualFree (chunk->start_of_memory, chunk->length, MEM_DECOMMIT);
+		g_assert (res);
+
+		GCMemChunk* prev = chunk;
+		chunk = chunk->next;
+		g_free (prev);
+	}
+	g_free (mp);
+}
+
 void
 mono_gc_base_init (void)
 {
 	if (gc_inited)
 		return;
 
+	mono_os_mutex_init (&nullgc_mutex);
+
 	mono_counters_init ();
 
 #ifndef HOST_WIN32
@@ -36,7 +126,7 @@ mono_gc_base_init (void)
 #endif
 
 	mono_thread_callbacks_init ();
-	mono_thread_info_init (sizeof (MonoThreadInfo));
+	mono_thread_info_init (sizeof (NullGCThreadInfo));
 
 	mono_thread_info_attach ();
 
@@ -190,22 +280,113 @@ mono_gc_make_root_descr_all_refs (int numbits)
 	return NULL;
 }
 
+#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
+
 void*
 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, void *key, const char *msg)
 {
-	return g_malloc0 (size);
+	//return g_malloc0 (size);
+	size += sizeof(size_t);
+	size = ALIGN_TO (size, mono_pagesize ());
+	char* ret = VirtualAlloc (0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+	*(size_t*)ret = size;
+	ret += sizeof (size_t);
+	return ret;
 }
 
 void
 mono_gc_free_fixed (void* addr)
 {
-	g_free (addr);
+	char* start = (char*)addr - sizeof (size_t);
+	size_t length = *(size_t*)start;
+	int res = VirtualFree (start, length, MEM_DECOMMIT);
+	g_assert (res);
+}
+
+/* only one can be enabled at a time */
+#define GUARD_PRELUDE 0
+#define GUARD_POSTLUDE 0
+
+static void*
+gc_mempool_alloc (MonoDomain* domain, size_t size)
+{
+	void* ret = NULL;
+	mono_os_mutex_lock (&nullgc_mutex);
+	GCMemPool* mp = domain->gc_mp;
+	if (!mp)
+		mp = domain->gc_mp = init_gc_mempool ();
+
+	// keep 16 byte alignment
+	size = ALIGN_TO (size, 16);
+
+#if GUARD_PRELUDE
+	GCMemChunk* chunk = g_new0 (GCMemChunk, 1);;
+	size_t chunk_size = MAX ((mp->page_size), ALIGN_TO (size, mp->page_size));
+	chunk_size += mp->page_size;
+
+	chunk->start_of_memory = chunk->current_memory = (char*)VirtualAlloc (0, chunk_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+	chunk->current_memory += mp->page_size;
+
+	DWORD old;
+	BOOL res = VirtualProtect (chunk->start_of_memory, mp->page_size, PAGE_NOACCESS, &old);
+	g_assert (res);
+
+	ret = chunk->current_memory;
+
+
+	chunk->length = chunk_size;
+
+	chunk->next = mp->chunks;
+	mp->chunks = chunk;
+#elif GUARD_POSTLUDE
+	GCMemChunk* chunk = g_new0 (GCMemChunk, 1);
+	size_t chunk_size = MAX ((mp->page_size), ALIGN_TO (size, mp->page_size));
+	chunk_size += mp->page_size;
+
+	chunk->start_of_memory = chunk->current_memory = (char*)VirtualAlloc (0, chunk_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+	/* offset to abut last page */
+	chunk->current_memory += (chunk_size - mp->page_size - size);
+
+	DWORD old;
+	BOOL res = VirtualProtect (chunk->start_of_memory + chunk_size - mp->page_size, mp->page_size, PAGE_NOACCESS, &old);
+	g_assert (res);
+
+	ret = chunk->current_memory;
+
+
+	chunk->length = chunk_size;
+
+	chunk->next = mp->chunks;
+	mp->chunks = chunk;
+#else
+
+	GCMemChunk* chunk = mp->chunks;
+	if (!chunk || ((chunk->current_memory + size) > (chunk->start_of_memory + chunk->length)))
+	{
+		chunk = g_new0 (GCMemChunk, 1);
+		size_t chunk_size = MAX ((4 * mp->page_size), ALIGN_TO (size, mp->page_size));
+		chunk->start_of_memory = chunk->current_memory = (char*)VirtualAlloc (0, chunk_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+		chunk->length = chunk_size;
+
+		chunk->next = mp->chunks;
+		mp->chunks = chunk;
+	}
+
+	ret = chunk->current_memory;
+	chunk->current_memory += size;
+
+	g_assert (chunk->current_memory <= (chunk->start_of_memory + chunk->length));
+#endif
+
+	mono_os_mutex_unlock (&nullgc_mutex);
+
+	return ret;
 }
 
 void *
 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
 {
-	MonoObject *obj = g_calloc (1, size);
+	MonoObject *obj = gc_mempool_alloc (vtable->domain, size);
 
 	obj->vtable = vtable;
 
@@ -215,7 +396,7 @@ mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
 void *
 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
 {
-	MonoArray *obj = g_calloc (1, size);
+	MonoArray *obj = gc_mempool_alloc (vtable->domain, size);
 
 	obj->obj.vtable = vtable;
 	obj->max_length = max_length;
@@ -226,7 +407,7 @@ mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
 void *
 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
 {
-	MonoArray *obj = g_calloc (1, size);
+	MonoArray *obj = gc_mempool_alloc (vtable->domain, size);
 
 	obj->obj.vtable = vtable;
 	obj->max_length = max_length;
@@ -240,7 +421,7 @@ mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uint
 void *
 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
 {
-	MonoString *obj = g_calloc (1, size);
+	MonoString *obj = gc_mempool_alloc (vtable->domain, size);
 
 	obj->object.vtable = vtable;
 	obj->length = len;
@@ -317,25 +498,25 @@ mono_gc_is_critical_method (MonoMethod *method)
 }
 
 gpointer
-mono_gc_thread_attach (MonoThreadInfo* info)
+mono_gc_thread_attach (NullGCThreadInfo* info)
 {
-	info->handle_stack = mono_handle_stack_alloc ();
+	info->info.handle_stack = mono_handle_stack_alloc ();
 	return info;
 }
 
 void
-mono_gc_thread_detach (MonoThreadInfo *p)
+mono_gc_thread_detach (NullGCThreadInfo*p)
 {
 }
 
 void
-mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
+mono_gc_thread_detach_with_lock (NullGCThreadInfo*p)
 {
-	mono_handle_stack_free (p->handle_stack);
+	mono_handle_stack_free (p->info.handle_stack);
 }
 
 gboolean
-mono_gc_thread_in_critical_region (MonoThreadInfo *info)
+mono_gc_thread_in_critical_region (NullGCThreadInfo*info)
 {
 	return FALSE;
 }
@@ -379,6 +560,7 @@ mono_gc_get_gc_name (void)
 void
 mono_gc_clear_domain (MonoDomain *domain)
 {
+	gc_free_mempool (domain->gc_mp);
 }
 
 void
@@ -595,6 +777,226 @@ mono_gc_pending_finalizers (void)
 	return FALSE;
 }
 
+void
+mono_gc_register_obj_with_weak_fields (void* obj)
+{
+	g_error ("Weak fields not supported by null gc");
+}
+
+static gboolean
+nullgc_is_thread_in_current_stw (NullGCThreadInfo* info, int* reason)
+{
+	/*
+	A thread explicitly asked to be skiped because it holds no managed state.
+	This is used by TP and finalizer threads.
+	FIXME Use an atomic variable for this to avoid everyone taking the GC LOCK.
+	*/
+	if (info->gc_disabled) {
+		if (reason)
+			*reason = 1;
+		return FALSE;
+	}
+
+	/*
+	We have detected that this thread is failing/dying, ignore it.
+	FIXME: can't we merge this with thread_is_dying?
+	*/
+	if (info->skip) {
+		if (reason)
+			*reason = 2;
+		return FALSE;
+	}
+
+	/*
+	Suspending the current thread will deadlock us, bad idea.
+	*/
+	if (info == mono_thread_info_current ()) {
+		if (reason)
+			*reason = 3;
+		return FALSE;
+	}
+
+	/*
+	We can't suspend the workers that will do all the heavy lifting.
+	FIXME Use some state bit in SgenThreadInfo for this.
+	*/
+	//if (sgen_thread_pool_is_thread_pool_thread (mono_thread_info_get_tid (info))) {
+	//	if (reason)
+	//		*reason = 4;
+	//	return FALSE;
+	//}
+
+	/*
+	The thread has signaled that it started to detach, ignore it.
+	FIXME: can't we merge this with skip
+	*/
+	if (!mono_thread_info_is_live (info)) {
+		if (reason)
+			*reason = 5;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+#define THREADS_STW_DEBUG(...)
+
+void
+nullgc_unified_suspend_stop_world (void)
+{
+	int sleep_duration = -1;
+
+	mono_threads_begin_global_suspend ();
+	//	THREADS_STW_DEBUG ("[GC-STW-BEGIN][%p] *** BEGIN SUSPEND *** \n", mono_thread_info_get_tid (mono_thread_info_current ()));
+
+	FOREACH_THREAD (info) {
+		info->skip = FALSE;
+		info->suspend_done = FALSE;
+
+		int reason;
+		if (!nullgc_is_thread_in_current_stw (info, &reason)) {
+			THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %s reason %d\n", mono_thread_info_get_tid (info), info->skip ? "true" : "false", reason);
+			continue;
+		}
+
+		info->skip = !mono_thread_info_begin_suspend (info);
+
+		THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false");
+	} FOREACH_THREAD_END
+
+		mono_thread_info_current ()->suspend_done = TRUE;
+		mono_threads_wait_pending_operations ();
+
+	for (;;) {
+		gint restart_counter = 0;
+
+		FOREACH_THREAD (info) {
+			gint suspend_count;
+
+			int reason = 0;
+			if (info->suspend_done || !nullgc_is_thread_in_current_stw (info, &reason)) {
+				THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason);
+				continue;
+			}
+
+			/*
+			All threads that reach here are pristine suspended. This means the following:
+
+			- We haven't accepted the previous suspend as good.
+			- We haven't gave up on it for this STW (it's either bad or asked not to)
+			*/
+			if (!mono_thread_info_in_critical_location (info)) {
+				info->suspend_done = TRUE;
+
+				THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info));
+				continue;
+			}
+
+			suspend_count = mono_thread_info_suspend_count (info);
+			if (!(suspend_count == 1))
+				g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count);
+
+			info->skip = !mono_thread_info_begin_resume (info);
+			if (!info->skip)
+				restart_counter += 1;
+
+			THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false");
+		} FOREACH_THREAD_END
+
+			mono_threads_wait_pending_operations ();
+
+		if (restart_counter == 0)
+			break;
+
+		if (sleep_duration < 0) {
+			mono_thread_info_yield ();
+			sleep_duration = 0;
+		}
+		else {
+			g_usleep (sleep_duration);
+			sleep_duration += 10;
+		}
+
+		FOREACH_THREAD (info) {
+			int reason = 0;
+			if (info->suspend_done || !nullgc_is_thread_in_current_stw (info, &reason)) {
+				THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason);
+				continue;
+			}
+
+			if (!mono_thread_info_is_running (info)) {
+				THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not running\n", mono_thread_info_get_tid (info));
+				continue;
+			}
+
+			/*info->client_info.skip = !*/mono_thread_info_begin_suspend (info);
+
+			//			THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false");
+		} FOREACH_THREAD_END
+
+			mono_threads_wait_pending_operations ();
+	}
+
+	FOREACH_THREAD (info) {
+		gpointer stopped_ip;
+
+		int reason = 0;
+		if (!nullgc_is_thread_in_current_stw (info, &reason)) {
+			//g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ());
+
+			THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason);
+			continue;
+		}
+
+		g_assert (info->suspend_done);
+
+		//info->ctx = mono_thread_info_get_suspend_state (info)->ctx;
+
+		/* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */
+		//info->client_info.stack_start = (gpointer)((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE);
+
+		//if (info->client_info.stack_start < info->client_info.info.stack_start_limit
+		//	|| info->client_info.stack_start >= info->client_info.info.stack_end) {
+		//	/*
+		//	 * Thread context is in unhandled state, most likely because it is
+		//	 * dying. We don't scan it.
+		//	 * FIXME We should probably rework and check the valid flag instead.
+		//	 */
+		//	info->client_info.stack_start = NULL;
+		//}
+
+		//stopped_ip = (gpointer)(MONO_CONTEXT_GET_IP (&info->client_info.ctx));
+
+		//binary_protocol_thread_suspend ((gpointer)mono_thread_info_get_tid (info), stopped_ip);
+
+		//THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended, stopped_ip = %p, stack = %p -> %p\n",
+		//	mono_thread_info_get_tid (info), stopped_ip, info->stack_start, info->stack_start ? info->info.stack_end : NULL);
+	} FOREACH_THREAD_END
+}
+
+void
+nullgc_unified_suspend_restart_world (void)
+{
+	THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n");
+	FOREACH_THREAD (info) {
+		int reason = 0;
+		if (nullgc_is_thread_in_current_stw (info, &reason)) {
+			g_assert (mono_thread_info_begin_resume (info));
+			THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] RESUME thread %p\n", mono_thread_info_get_tid (info));
+
+			//binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
+		}
+		else {
+			THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p, reason %d\n", mono_thread_info_get_tid (info), reason);
+		}
+	} FOREACH_THREAD_END
+
+		mono_threads_wait_pending_operations ();
+	mono_threads_end_global_suspend ();
+}
+
+
 #else
 
 MONO_EMPTY_SOURCE_FILE (null_gc);
diff --git a/mono/metadata/unity-liveness.c b/mono/metadata/unity-liveness.c
index 7f6c3df8f670..b97aa33446c3 100644
--- a/mono/metadata/unity-liveness.c
+++ b/mono/metadata/unity-liveness.c
@@ -30,8 +30,8 @@ void GC_start_world_external()
 	g_assert_not_reached ();
 }
 #endif
+#elif defined(HAVE_NULL_GC)
 #else
-#error need to implement liveness GC API
 #endif
 
 custom_growable_array* array_create_and_initialize (guint capacity)
@@ -634,6 +634,8 @@ void mono_unity_liveness_free_struct (LivenessState* state)
 	g_free(state);
 }
 
+void nullgc_unified_suspend_stop_world ();
+void nullgc_unified_suspend_restart_world ();
 void mono_unity_liveness_stop_gc_world (LivenessState* state)
 {
 	state->onWorldStopCallback();
@@ -641,6 +643,8 @@ void mono_unity_liveness_stop_gc_world (LivenessState* state)
 	sgen_stop_world (1);
 #elif defined(HAVE_BOEHM_GC)
 	GC_stop_world_external ();
+#elif defined(HAVE_NULL_GC)
+	nullgc_unified_suspend_stop_world ();
 #else
 #error need to implement liveness GC API
 #endif
@@ -652,6 +656,8 @@ void mono_unity_liveness_start_gc_world (LivenessState* state)
 	sgen_restart_world (1);
 #elif defined(HAVE_BOEHM_GC)
 	GC_start_world_external ();
+#elif defined(HAVE_NULL_GC)
+	nullgc_unified_suspend_restart_world ();
 #else
 #error need to implement liveness GC API
 #endif
diff --git a/mono/metadata/unity-memory-info.c b/mono/metadata/unity-memory-info.c
index a7dab80c797b..2099bd014477 100644
--- a/mono/metadata/unity-memory-info.c
+++ b/mono/metadata/unity-memory-info.c
@@ -804,4 +804,10 @@ void mono_unity_free_captured_memory_snapshot(MonoManagedMemorySnapshot* snapsho
 	g_free(snapshot);
 }
 
+void
+mono_unity_class_for_each (ClassReportFunc callback, void* user_data)
+{
+
+}
+
 #endif
\ No newline at end of file
diff --git a/mono/metadata/unity-utils.c b/mono/metadata/unity-utils.c
index d0ef772045c0..8280d8faa884 100644
--- a/mono/metadata/unity-utils.c
+++ b/mono/metadata/unity-utils.c
@@ -943,6 +943,7 @@ MONO_API void mono_unity_gc_set_mode(MonoGCMode mode)
 			GC_set_disable_automatic_collection(TRUE);
 			break;
 	}
+#elif defined(HAVE_NULL_GC)
 #else
 	g_assert_not_reached ();
 #endif
@@ -963,6 +964,7 @@ MONO_API void mono_unity_gc_disable()
 {
 #if HAVE_BDWGC_GC
 	GC_disable();
+#elif defined(HAVE_NULL_GC)
 #else
 	g_assert_not_reached ();
 #endif
@@ -1852,11 +1854,15 @@ mono_unity_class_field_is_literal(MonoClassField *field)
 }
 
 // GC world control
+void nullgc_unified_suspend_stop_world ();
+void nullgc_unified_suspend_restart_world ();
 MONO_API void
 mono_unity_stop_gc_world()
 {
 #if HAVE_BDWGC_GC
 	GC_stop_world_external();
+#elif defined(HAVE_NULL_GC)
+	nullgc_unified_suspend_stop_world ();
 #else
 	g_assert_not_reached();
 #endif
@@ -1867,6 +1873,8 @@ mono_unity_start_gc_world()
 {
 #if HAVE_BDWGC_GC
 	GC_start_world_external();
+#elif defined(HAVE_NULL_GC)
+	nullgc_unified_suspend_restart_world ();
 #else
 	g_assert_not_reached();
 #endif
@@ -1893,6 +1901,7 @@ mono_unity_gc_heap_foreach(GFunc callback, gpointer user_data)
 	ctx.user_data = user_data;
 
 	GC_foreach_heap_section(&ctx, handle_gc_heap_chunk);
+#elif defined(HAVE_NULL_GC)
 #else
 	g_assert_not_reached();
 #endif
@@ -1914,6 +1923,7 @@ mono_unity_gc_handles_foreach_get_target(GFunc callback, gpointer user_data)
 	ctx.callback = callback;
 	ctx.user_data = user_data;
 	mono_gc_strong_handle_foreach(handle_gc_handle, &ctx);
+#elif defined(HAVE_NULL_GC)
 #else
 	g_assert_not_reached();
 #endif
diff --git a/msvc/mono.props b/msvc/mono.props
index b96720b8871a..303586d9738a 100644
--- a/msvc/mono.props
+++ b/msvc/mono.props
@@ -6,7 +6,7 @@
     <!-- Change this to custom distribution tree location to enable out of source tree distribution, example c:/mono-dist/ -->
     <MONO_INSTALL_DIR_PREFIX>$(MSBuildProjectDirectory)/./dist/</MONO_INSTALL_DIR_PREFIX>
     <!-- GC in use, sgen or boehm, default is sgen. -->
-    <MONO_TARGET_GC>bdwgc</MONO_TARGET_GC>
+    <MONO_TARGET_GC>nullgc</MONO_TARGET_GC>
     <!-- When true, build targets will get a suffix based on used GC. Makes it possible to have builds using different GC's in same build folders, sharing common targets. -->
     <MONO_USE_TARGET_SUFFIX>true</MONO_USE_TARGET_SUFFIX>
     <!-- When true, build will get a separate build folder based on used GC. Makes it possible separate builds into different output folders under the same build prefix. -->
@@ -62,6 +62,12 @@
     <MONO_TARGET_SUFFIX Condition="'$(MONO_USE_TARGET_SUFFIX)'=='true'">-bdwgc</MONO_TARGET_SUFFIX>
     <MONO_BUILD_DIR_PREFIX Condition="'$(MONO_USE_SEPARATE_BUILD_DIR)'=='true'">$(MONO_BUILD_DIR_PREFIX)bdwgc/</MONO_BUILD_DIR_PREFIX>
   </PropertyGroup>
+  <PropertyGroup Label="NullGC" Condition="$(MONO_TARGET_GC)=='nullgc'">
+    <GC_DEFINES>HAVE_NULL_GC</GC_DEFINES>
+    <GC_LIB></GC_LIB>
+    <MONO_TARGET_SUFFIX Condition="'$(MONO_USE_TARGET_SUFFIX)'=='true'">-nullgc</MONO_TARGET_SUFFIX>
+    <MONO_BUILD_DIR_PREFIX Condition="'$(MONO_USE_SEPARATE_BUILD_DIR)'=='true'">$(MONO_BUILD_DIR_PREFIX)nullgc/</MONO_BUILD_DIR_PREFIX>
+  </PropertyGroup>
   <PropertyGroup Label="Static-Mono-Libraries">
     <MONO_RUNTIME_LIBS>libmonoutils.lib;libmonoruntime$(MONO_TARGET_SUFFIX).lib;libmini$(MONO_TARGET_SUFFIX).lib;$(GC_LIB)</MONO_RUNTIME_LIBS>
     <MONO_STATIC_LIBMONO_LIB>libmono-static$(MONO_TARGET_SUFFIX).lib</MONO_STATIC_LIBMONO_LIB>