diff --git a/src/array.c b/src/array.c
index fc28cdbe1e90a8dba0d443e6af909fcb52ec1316..663e52a27ec7e38281d951d843cb3c1a9d1e39b0 100644
--- a/src/array.c
+++ b/src/array.c
@@ -23,7 +23,7 @@
 #include "stuff.h"
 #include "bignum.h"
 
-RCSID("$Id: array.c,v 1.65 2000/04/17 16:48:57 grubba Exp $");
+RCSID("$Id: array.c,v 1.66 2000/04/20 01:49:42 mast Exp $");
 
 struct array empty_array=
 {
@@ -104,7 +104,7 @@ static void array_free_no_free(struct array *v)
 
   free((char *)v);
 
-  GC_FREE();
+  GC_FREE(v);
 }
 
 /*
diff --git a/src/gc.c b/src/gc.c
index 32685e813c0924a64889ba09572dea8693575eaf..0038baea05bc8e4f8674fb39af709c91ce1478f8 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -29,7 +29,7 @@ struct callback *gc_evaluator_callback=0;
 
 #include "block_alloc.h"
 
-RCSID("$Id: gc.c,v 1.73 2000/04/19 21:49:27 mast Exp $");
+RCSID("$Id: gc.c,v 1.74 2000/04/20 01:49:42 mast Exp $");
 
 /* Run garbage collect approximate every time we have
  * 20 percent of all arrays, objects and programs is
@@ -597,7 +597,7 @@ INT32 real_gc_check(void *a)
     return 0;
   }
 
-  if (Pike_in_gc != 1)
+  if (Pike_in_gc != 2)
     fatal("gc check attempted in pass %d.\n", Pike_in_gc);
 
   if(m->saved_refs != -1)
@@ -727,7 +727,7 @@ int debug_gc_is_referenced(void *a)
 
   if(m->refs + m->xrefs > *(INT32 *)a ||
      (!(m->refs < *(INT32 *)a) && m->xrefs)  ||
-     (Pike_in_gc < 3  && m->saved_refs != -1 && m->saved_refs != *(INT32 *)a))
+     (Pike_in_gc < 4 && m->saved_refs != -1 && m->saved_refs != *(INT32 *)a))
   {
     INT32 refs=m->refs;
     INT32 xrefs=m->xrefs;
@@ -811,7 +811,7 @@ int gc_mark(void *a)
   m=get_marker(debug_malloc_pass(a));
 
 #ifdef PIKE_DEBUG
-  if (Pike_in_gc != 2)
+  if (Pike_in_gc != 3)
     fatal("gc mark attempted in pass %d.\n", Pike_in_gc);
 #endif
 
@@ -863,6 +863,7 @@ void do_gc(void)
   double tmp;
   INT32 tmp2;
   double multiplier;
+  int destroyed, destructed;
 #ifdef HAVE_GETHRTIME
 #ifdef PIKE_DEBUG
   hrtime_t gcstarttime;
@@ -872,7 +873,10 @@ void do_gc(void)
   if(Pike_in_gc) return;
   Pike_in_gc=1;
 
-  /* Make sure there will be no callback to this while we're in the gc. */
+  /* Make sure there will be no callback to this while we're in the
+   * gc. That can be fatal since this function links objects back to
+   * the object list, which causes that list to be reordered and the
+   * various gc loops over it might then miss things. */
   destruct_objects_to_destruct();
 
   if(gc_evaluator_callback)
@@ -906,6 +910,7 @@ void do_gc(void)
 
   init_gc();
 
+  Pike_in_gc=2;
   /* First we count internal references */
   gc_check_all_arrays();
   gc_check_all_multisets();
@@ -927,8 +932,7 @@ void do_gc(void)
    */
   call_callback(& gc_callbacks, (void *)0);
 
-  Pike_in_gc=2;
-
+  Pike_in_gc=3;
   /* Next we mark anything with external references */
   gc_mark_all_arrays();
   run_queue(&gc_mark_queue);
@@ -944,27 +948,35 @@ void do_gc(void)
   if(d_flag)
     gc_mark_all_strings();
 
-
 #ifdef PIKE_DEBUG
   check_for=(void *)1;
 #endif
-  Pike_in_gc=3;
+  Pike_in_gc=5;
   /* Now we free the unused stuff */
   gc_free_all_unreferenced_arrays();
   gc_free_all_unreferenced_multisets();
   gc_free_all_unreferenced_mappings();
   gc_free_all_unreferenced_programs();
-  gc_free_all_unreferenced_objects();
+  Pike_in_gc=4;
+  /* This is intended to happen before the freeing done above. But
+   * it's put here for the time being, since the problem of non-object
+   * objects getting external references from destroy code isn't
+   * solved yet. */
+  destroyed = gc_destroy_all_unreferenced_objects();
+  Pike_in_gc=5;
+  destructed = gc_free_all_unreferenced_objects();
 
 #ifdef PIKE_DEBUG
-
+  if (destroyed != destructed)
+    fatal("destroy() called in %d objects in gc, but %d destructed.\n",
+	  destroyed, destructed);
   check_for=0;
   if(fatal_after_gc) fatal(fatal_after_gc);
 #endif
 
   exit_gc();
 
-  Pike_in_gc=5;
+  Pike_in_gc=6;
   destruct_objects_to_destruct();
   
   objects_freed -= (double) num_objects;
diff --git a/src/gc.h b/src/gc.h
index b539f0ffa816c0a3339c4ed4c346f4b7d8fa1273..00cd2a2039bfbd96862279f16e2e2f21e8bee932 100644
--- a/src/gc.h
+++ b/src/gc.h
@@ -1,5 +1,5 @@
 /*
- * $Id: gc.h,v 1.36 2000/04/19 21:25:33 mast Exp $
+ * $Id: gc.h,v 1.37 2000/04/20 01:49:43 mast Exp $
  */
 #ifndef GC_H
 #define GC_H
@@ -32,7 +32,7 @@ extern void *gc_svalue_location;
  num_allocs++;								\
  DO_IF_DEBUG(								\
    if(d_flag) CHECK_INTERPRETER_LOCK();					\
-   if(Pike_in_gc >0 && Pike_in_gc<4)					\
+   if(Pike_in_gc > 1 && Pike_in_gc < 4 && Pike_in_gc != 5)		\
      fatal("Allocating new objects within gc is not allowed!\n");	\
  )									\
  if(num_allocs == alloc_threshold && !gc_evaluator_callback)		\
@@ -43,10 +43,10 @@ extern void *gc_svalue_location;
 struct marker
 {
   struct marker *next;
-  INT32 refs;
+  INT32 refs;			/* Internal references. */
 #ifdef PIKE_DEBUG
-  INT32 xrefs;
-  INT32 saved_refs;
+  INT32 xrefs;			/* Known external references. */
+  INT32 saved_refs;		/* Object refcount during check and mark pass. */
 #endif
   INT32 flags;
   void *data;
@@ -85,24 +85,19 @@ void f__gc_status(INT32 args);
 #define gc_check(VP) real_gc_check(debug_malloc_pass(VP))
 
 #ifdef PIKE_DEBUG
-#define LOW_GC_FREE() do {						\
+#define LOW_GC_FREE(OBJ) do {						\
   extern int d_flag;							\
   if(d_flag) CHECK_INTERPRETER_LOCK();					\
   num_objects-- ;							\
   if(num_objects < 0)							\
     fatal("Panic!! less than zero objects!\n");				\
+  if (Pike_in_gc) remove_marker(OBJ);					\
 }while(0)
 
-#define GC_FREE() do {							\
-  if(Pike_in_gc >0 && Pike_in_gc<3)					\
+#define GC_FREE(OBJ) do {						\
+  if(Pike_in_gc >2 && Pike_in_gc <4)					\
     fatal("Freeing objects within gc is not allowed!\n");		\
-  LOW_GC_FREE();							\
-}while(0)
-
-#define GC_FREE_OBJ() do {						\
-  if(Pike_in_gc >1 && Pike_in_gc<4)					\
-    fatal("Freeing objects within gc is not allowed!\n");		\
-  LOW_GC_FREE();							\
+  LOW_GC_FREE(OBJ);							\
 }while(0)
 
 #else
@@ -110,9 +105,11 @@ void f__gc_status(INT32 args);
 #define debug_gc_check_short_svalue(S,N,T,V) gc_check_short_svalue((S),N)
 #define debug_gc_xmark_svalues(S,N,X) gc_xmark_svalues((S),N)
 #define debug_gc_check(VP,T,V) gc_check((VP))
-#define LOW_GC_FREE() do {} while(0)
-#define GC_FREE() do { num_objects-- ; }while(0)
-#define GC_FREE_OBJ() do { num_objects-- ; }while(0)
+#define LOW_GC_FREE(OBJ) do {						\
+  num_objects-- ;							\
+  if (Pike_in_gc) remove_marker(OBJ);					\
+}while(0)
+#define GC_FREE(OBJ) LOW_GC_FREE(OBJ)
 #endif
 
 
@@ -126,8 +123,8 @@ void f__gc_status(INT32 args);
 #define GC_XREFERENCED 2
 #define GC_CHECKED 4
 #ifdef PIKE_DEBUG
-#define GC_DO_FREE_OBJ 8
-#define GC_OBJ_PASS_4 16
+#define GC_OBJ_DESTROY_CHECK 8
+#define GC_DO_FREE_OBJ 16
 #endif
 
 #ifdef PIKE_DEBUG
diff --git a/src/mapping.c b/src/mapping.c
index ac6d0ad37d6ae8b0f4f0a6dd0e99c96c117fbce9..1fdfd11eb076ff31945ca45eeba0d4b263f44e24 100644
--- a/src/mapping.c
+++ b/src/mapping.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: mapping.c,v 1.77 2000/04/17 16:42:16 grubba Exp $");
+RCSID("$Id: mapping.c,v 1.78 2000/04/20 01:49:43 mast Exp $");
 #include "main.h"
 #include "object.h"
 #include "mapping.h"
@@ -63,7 +63,7 @@ DO_IF_DEBUG(								\
 									\
   if(m->next) m->next->prev = m->prev;					\
 									\
-  GC_FREE();
+  GC_FREE(m);
 
 
 #undef COUNT_OTHER
diff --git a/src/multiset.c b/src/multiset.c
index 43ce3be3f228232b351b5b5fe24ca53dc383c981..4cbca589753fa55a367bc5c802d2a1aa3ee529fd 100644
--- a/src/multiset.c
+++ b/src/multiset.c
@@ -16,7 +16,7 @@
 #include "gc.h"
 #include "security.h"
 
-RCSID("$Id: multiset.c,v 1.18 2000/04/12 18:40:12 hubbe Exp $");
+RCSID("$Id: multiset.c,v 1.19 2000/04/20 01:49:43 mast Exp $");
 
 struct multiset *first_multiset;
 
@@ -66,7 +66,7 @@ void really_free_multiset(struct multiset *l)
   if(l->next)  l->next->prev = l->prev;
     
   free((char *)l);
-  GC_FREE();
+  GC_FREE(l);
 }
 
 
diff --git a/src/object.c b/src/object.c
index e34869f87f1026b88c51c2539d4c481807a55228..8152f6a5d54bb3cbe63b6d1c4a83ee9d2a8e9f8c 100644
--- a/src/object.c
+++ b/src/object.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: object.c,v 1.115 2000/04/19 21:49:27 mast Exp $");
+RCSID("$Id: object.c,v 1.116 2000/04/20 01:49:43 mast Exp $");
 #include "object.h"
 #include "dynamic_buffer.h"
 #include "interpret.h"
@@ -471,7 +471,7 @@ void low_destruct(struct object *o,int do_free)
 
 #ifdef PIKE_DEBUG
   if(d_flag > 20) do_debug();
-  if(Pike_in_gc && Pike_in_gc<4)
+  if(Pike_in_gc > 1 && Pike_in_gc < 4 && Pike_in_gc != 5)
     fatal("Destructing object inside gc()\n");
 #endif
 
@@ -561,7 +561,7 @@ void destruct_objects_to_destruct(void)
   struct object *o, *next;
 
 #ifdef PIKE_DEBUG
-  if (Pike_in_gc >= 3 && Pike_in_gc <= 4)
+  if (Pike_in_gc > 1 && Pike_in_gc < 6)
     fatal("Can't meddle with the object link list in gc pass %d.\n", Pike_in_gc);
 #endif
 
@@ -612,7 +612,10 @@ void really_free_object(struct object *o)
   if(o->prog)
   {
     DOUBLELINK(objects_to_destruct,o);
-    if (Pike_in_gc) return;	/* Done last in gc(). */
+    if (Pike_in_gc) {
+      remove_marker(o);
+      if (Pike_in_gc < 6) return; /* Done last in gc(). */
+    }
     if(!destruct_object_evaluator_callback)
     {
       destruct_object_evaluator_callback=
@@ -633,9 +636,7 @@ void really_free_object(struct object *o)
 
     free((char *)o);
 
-    /* Not using GC_FREE_OBJ here, since it balks in gc pass 3. This
-     * case is ok, since no destroy() is called. */
-    LOW_GC_FREE();
+    GC_FREE(o);
   }
 }
 
@@ -1223,10 +1224,10 @@ void gc_mark_all_objects(void)
 
 }
 
-void gc_free_all_unreferenced_objects(void)
+int gc_destroy_all_unreferenced_objects(void)
 {
+  int n = 0;
   struct object *o,*next;
-  extern int Pike_in_gc;
 
 #ifdef PIKE_DEBUG
   if(d_flag)
@@ -1245,12 +1246,10 @@ void gc_free_all_unreferenced_objects(void)
   }
 #endif
 
-  Pike_in_gc=4;  /* Allow thread switches, god help us */
-
   for(o=first_object;o;o=o->next)
   {
 #ifdef PIKE_DEBUG
-    get_marker(o)->flags |= GC_OBJ_PASS_4;
+    get_marker(o)->flags |= GC_OBJ_DESTROY_CHECK;
 #endif
     if(gc_do_free(o))
     {
@@ -1259,8 +1258,34 @@ void gc_free_all_unreferenced_objects(void)
       get_marker(o)->flags |= GC_DO_FREE_OBJ;
 #endif
       call_destroy(o,0);
+      n++;
+    }
+  }
+
+  return n;
+}
+
+int gc_free_all_unreferenced_objects(void)
+{
+  int n = 0;
+  struct object *o,*next;
+
+#ifdef PIKE_DEBUG
+  if(d_flag)
+  {
+    for(o=first_object;o;o=next)
+    {
+      if(!gc_do_free(o))
+      {
+	add_ref(o);
+	gc_check_object(o);
+	SET_NEXT_AND_FREE(o,free_object);
+      }else{
+	next=o->next;
+      }
     }
   }
+#endif
 
   for(o=first_object;o;o=next)
   {
@@ -1268,7 +1293,7 @@ void gc_free_all_unreferenced_objects(void)
     {
 #ifdef PIKE_DEBUG
       if (!(get_marker(o)->flags & GC_DO_FREE_OBJ) ||
-	  !(get_marker(o)->flags & GC_OBJ_PASS_4)) {
+	  !(get_marker(o)->flags & GC_OBJ_DESTROY_CHECK)) {
 	extern char *fatal_after_gc;
 	fprintf(stderr,"**Object unexpectedly marked for gc. flags: %d\n",
 		get_marker(o)->flags);
@@ -1276,14 +1301,19 @@ void gc_free_all_unreferenced_objects(void)
 	locate_references(o);
 	fprintf(stderr,"##### Continuing search for more bugs....\n");
 	fatal_after_gc="Object unexpectedly marked for gc.\n";
+	next=o->next;		/* Leave it alone and continue. */
+	continue;
       }
 #endif
       low_destruct(o,1);
+      n++;
       SET_NEXT_AND_FREE(o,free_object);
     }else{
       next=o->next;
     }
   }
+
+  return n;
 }
 
 void count_memory_in_objects(INT32 *num_, INT32 *size_)
diff --git a/src/object.h b/src/object.h
index f564caa1b26012723287aa775545ab9a4bbcfc0a..30048971e506f2f7421fd29c3c098595b0ab6d7d 100644
--- a/src/object.h
+++ b/src/object.h
@@ -5,7 +5,7 @@
 \*/
 
 /*
- * $Id: object.h,v 1.43 2000/04/19 21:49:27 mast Exp $
+ * $Id: object.h,v 1.44 2000/04/20 01:49:44 mast Exp $
  */
 #ifndef OBJECT_H
 #define OBJECT_H
@@ -96,7 +96,8 @@ struct array *object_values(struct object *o);
 void gc_mark_object_as_referenced(struct object *o);
 void gc_check_all_objects(void);
 void gc_mark_all_objects(void);
-void gc_free_all_unreferenced_objects(void);
+int gc_destroy_all_unreferenced_objects(void);
+int gc_free_all_unreferenced_objects(void);
 void count_memory_in_objects(INT32 *num_, INT32 *size_);
 struct magic_index_struct;
 void push_magic_index(struct program *type, int inherit_no, int parent_level);
diff --git a/src/program.c b/src/program.c
index 96bf3abb947886f99fb248a432baa8472acf07e5..de0d0ac8772cfa3df2c9bbb0f507f4eb3fd9b7f9 100644
--- a/src/program.c
+++ b/src/program.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: program.c,v 1.230 2000/04/19 16:05:19 mast Exp $");
+RCSID("$Id: program.c,v 1.231 2000/04/20 01:49:44 mast Exp $");
 #include "program.h"
 #include "object.h"
 #include "dynamic_buffer.h"
@@ -971,7 +971,7 @@ void really_free_program(struct program *p)
   FREE_PROT(p);
   dmfree((char *)p);
 
-  GC_FREE();
+  GC_FREE(p);
 }
 
 #ifdef PIKE_DEBUG
diff --git a/src/threads.h b/src/threads.h
index ed6ad41d2fa6a40ac1c8dd13165bf72636eaacd4..256559d1f5cd2e2afdc2a991cee548345388a32c 100644
--- a/src/threads.h
+++ b/src/threads.h
@@ -1,5 +1,5 @@
 /*
- * $Id: threads.h,v 1.86 2000/04/19 16:03:31 mast Exp $
+ * $Id: threads.h,v 1.87 2000/04/20 01:49:45 mast Exp $
  */
 #ifndef THREADS_H
 #define THREADS_H
@@ -506,7 +506,7 @@ struct thread_state {
      DO_IF_DEBUG({ \
        if(thread_for_id(th_self()) != thread_id) \
 	 fatal("thread_for_id() (or thread_id) failed! %p != %p\n",thread_for_id(th_self()),thread_id); \
-       if (Pike_in_gc >0 && Pike_in_gc <4) \
+       if (Pike_in_gc >1 && Pike_in_gc <4) \
 	 fatal("Threads allowed during garbage collection.\n"); \
      }) \
      if(num_threads > 1 && !threads_disabled) { \
@@ -541,7 +541,7 @@ struct thread_state {
      DO_IF_DEBUG({ \
        if(thread_for_id(th_self()) != thread_id) \
 	 fatal("thread_for_id() (or thread_id) failed! %p != %p\n",thread_for_id(th_self()),thread_id); \
-       if (Pike_in_gc >0 && Pike_in_gc <4) \
+       if (Pike_in_gc >1 && Pike_in_gc <4) \
 	 fatal("Threads allowed during garbage collection.\n"); \
      }) \
      if(num_threads > 1 && !threads_disabled) { \