diff --git a/src/array.c b/src/array.c
index 88bf09f4b28c9b358918ba0e894c6f3cd269c387..7a80d8ea786170fa9a19335fa4ae46b80bfcfade 100644
--- a/src/array.c
+++ b/src/array.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: array.c,v 1.154 2004/03/15 22:23:14 mast Exp $
+|| $Id: array.c,v 1.155 2004/03/15 22:47:15 mast Exp $
 */
 
 #include "global.h"
@@ -26,7 +26,7 @@
 #include "cyclic.h"
 #include "multiset.h"
 
-RCSID("$Id: array.c,v 1.154 2004/03/15 22:23:14 mast Exp $");
+RCSID("$Id: array.c,v 1.155 2004/03/15 22:47:15 mast Exp $");
 
 PMOD_EXPORT struct array empty_array=
 {
@@ -2350,7 +2350,8 @@ void real_gc_cycle_check_array(struct array *a, int weak)
 {
   GC_CYCLE_ENTER(a, T_ARRAY, weak) {
 #ifdef PIKE_DEBUG
-    if (a == &empty_array || a == &weak_empty_array || a == &weak_shrink_empty_array)
+    if (!gc_destruct_everything &&
+	(a == &empty_array || a == &weak_empty_array || a == &weak_shrink_empty_array))
       Pike_fatal("Trying to gc cycle check some *_empty_array.\n");
 #endif
 
diff --git a/src/gc.c b/src/gc.c
index b0d9aaaf8ae296a606dec1737d9eb9ca1cd95495..3a0f69a0335591973fa1dd4b7c72063881e17f3d 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: gc.c,v 1.241 2004/03/15 22:23:14 mast Exp $
+|| $Id: gc.c,v 1.242 2004/03/15 22:47:15 mast Exp $
 */
 
 #include "global.h"
@@ -33,7 +33,7 @@ struct callback *gc_evaluator_callback=0;
 
 #include "block_alloc.h"
 
-RCSID("$Id: gc.c,v 1.241 2004/03/15 22:23:14 mast Exp $");
+RCSID("$Id: gc.c,v 1.242 2004/03/15 22:47:15 mast Exp $");
 
 int gc_enabled = 1;
 
@@ -108,6 +108,11 @@ PMOD_EXPORT int Pike_in_gc = 0;
 int gc_generation = 0;
 time_t last_gc;
 int gc_trace = 0, gc_debug = 0;
+#ifdef DO_PIKE_CLEANUP
+int gc_destruct_everything = 0;
+#else
+#define gc_destruct_everything 0
+#endif
 
 struct gc_frame
 {
@@ -1396,6 +1401,8 @@ void debug_gc_touch(void *a)
 #ifdef PIKE_DEBUG
 	else if (m->flags & GC_MARKED)
 	  return;
+	else if (gc_destruct_everything)
+	  return;
 	else if (!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_XREFERENCED)
 	  gc_fatal(a, 3, "A thing with external references "
 		   "got missed by mark pass.\n");
@@ -2179,12 +2186,14 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
     Pike_fatal("GC cycle push attempted in invalid pass.\n");
   if (gc_debug && !(m->flags & GC_PRETOUCHED))
     gc_fatal(x, 0, "gc_cycle_push() called for untouched thing.\n");
-  if ((!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED) &&
-      *(INT32 *) x)
-    gc_fatal(x, 1, "Got a referenced marker to gc_cycle_push.\n");
-  if (m->flags & GC_XREFERENCED)
-    gc_fatal(x, 1, "Doing cycle check in externally referenced thing "
-	     "missed in mark pass.\n");
+  if (!gc_destruct_everything) {
+    if ((!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED) &&
+	*(INT32 *) x)
+      gc_fatal(x, 1, "Got a referenced marker to gc_cycle_push.\n");
+    if (m->flags & GC_XREFERENCED)
+      gc_fatal(x, 1, "Doing cycle check in externally referenced thing "
+	       "missed in mark pass.\n");
+  }
   if (weak && gc_rec_last == &rec_list)
     gc_fatal(x, 1, "weak is %d when on top of stack.\n", weak);
   if (gc_debug > 1) {
@@ -2457,12 +2466,14 @@ static void gc_cycle_pop(void *a)
     Pike_fatal("GC cycle pop attempted in invalid pass.\n");
   if (!(m->flags & GC_CYCLE_CHECKED))
     gc_fatal(a, 0, "Marker being popped doesn't have GC_CYCLE_CHECKED.\n");
-  if ((!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED) &&
-      *(INT32 *) a)
-    gc_fatal(a, 1, "Got a referenced marker to gc_cycle_pop.\n");
-  if (m->flags & GC_XREFERENCED)
-    gc_fatal(a, 1, "Doing cycle check in externally referenced thing "
-	     "missed in mark pass.\n");
+  if (!gc_destruct_everything) {
+    if ((!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED) &&
+	*(INT32 *) a)
+      gc_fatal(a, 1, "Got a referenced marker to gc_cycle_pop.\n");
+    if (m->flags & GC_XREFERENCED)
+      gc_fatal(a, 1, "Doing cycle check in externally referenced thing "
+	       "missed in mark pass.\n");
+  }
 #endif
 #ifdef GC_CYCLE_DEBUG
   gc_cycle_indent -= 2;
@@ -2576,16 +2587,33 @@ int gc_do_free(void *a)
   m=find_marker(debug_malloc_pass(a));
   if (!m) return 0;		/* Object created after cycle pass. */
 
+  if (gc_destruct_everything) {
+    /* We don't actually free much in this mode, just destruct
+     * objects. So when we normally would return nonzero we just
+     * remove the extra ref again. */
+    if (!(m->flags & GC_LIVE)) {
+      if (*(INT32 *) a == 1)
+	return 1;
+      else {
+	gc_free_extra_ref (a);
+	--*(INT32 *) a;
+      }
+    }
+    return 0;
+  }
+
 #ifdef PIKE_DEBUG
   if (*(INT32 *) a > !!(m->flags & GC_GOT_EXTRA_REF)) {
-    if (!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED)
+    if (!gc_destruct_everything &&
+	(!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED))
       gc_fatal(a, 0, "gc_do_free() called for referenced thing.\n");
     if (gc_debug &&
 	(m->flags & (GC_PRETOUCHED|GC_MARKED|GC_IS_REFERENCED)) == GC_PRETOUCHED)
       gc_fatal(a, 0, "gc_do_free() called without prior call to "
 	       "gc_mark() or gc_is_referenced().\n");
   }
-  if((m->flags & (GC_MARKED|GC_XREFERENCED)) == GC_XREFERENCED)
+  if(!gc_destruct_everything &&
+     (m->flags & (GC_MARKED|GC_XREFERENCED)) == GC_XREFERENCED)
     gc_fatal(a, 1, "Thing with external reference missed in gc mark pass.\n");
   if ((m->flags & (GC_DO_FREE|GC_LIVE)) == GC_LIVE) live_ref++;
   m->flags |= GC_DO_FREE;
@@ -2797,30 +2825,36 @@ size_t do_gc(void *ignored, int explicit_call)
   gc_internal_program = first_program;
   gc_internal_object = first_object;
 
-  /* Next we mark anything with external references. Note that we can
-   * follow the same reference several times, e.g. with shared mapping
-   * data blocks. */
-  ACCEPT_UNFINISHED_TYPE_FIELDS {
-    gc_mark_all_arrays();
-    gc_mark_run_queue();
-    gc_mark_all_multisets();
-    gc_mark_run_queue();
-    gc_mark_all_mappings();
-    gc_mark_run_queue();
-    gc_mark_all_programs();
-    gc_mark_run_queue();
-    gc_mark_all_objects();
-    gc_mark_run_queue();
-#ifdef PIKE_DEBUG
-    if(gc_debug) gc_mark_all_strings();
+  if (gc_destruct_everything) {
+    GC_VERBOSE_DO(fprintf(stderr,
+			  "| mark pass skipped - will destruct all objects\n"));
+  }
+  else {
+    /* Next we mark anything with external references. Note that we can
+     * follow the same reference several times, e.g. with shared mapping
+     * data blocks. */
+    ACCEPT_UNFINISHED_TYPE_FIELDS {
+      gc_mark_all_arrays();
+      gc_mark_run_queue();
+      gc_mark_all_multisets();
+      gc_mark_run_queue();
+      gc_mark_all_mappings();
+      gc_mark_run_queue();
+      gc_mark_all_programs();
+      gc_mark_run_queue();
+      gc_mark_all_objects();
+      gc_mark_run_queue();
+#ifdef PIKE_DEBUG
+      if(gc_debug) gc_mark_all_strings();
 #endif /* PIKE_DEBUG */
-  } END_ACCEPT_UNFINISHED_TYPE_FIELDS;
+    } END_ACCEPT_UNFINISHED_TYPE_FIELDS;
 
-  GC_VERBOSE_DO(fprintf(stderr,
-			"| mark: %u markers referenced, %u weak references freed,\n"
-			"|       %d things to free, "
-			"got %"PRINTSIZET"u tricky weak refs\n",
-			marked, weak_freed, delayed_freed, gc_ext_weak_refs));
+    GC_VERBOSE_DO(fprintf(stderr,
+			  "| mark: %u markers referenced, %u weak references freed,\n"
+			  "|       %d things to free, "
+			  "got %"PRINTSIZET"u tricky weak refs\n",
+			  marked, weak_freed, delayed_freed, gc_ext_weak_refs));
+  }
 
   {
 #ifdef PIKE_DEBUG
@@ -2987,7 +3021,8 @@ size_t do_gc(void *ignored, int explicit_call)
   /* Destruct the live objects in cycles, but first warn about any bad
    * cycles. */
   pre_kill_objs = num_objects;
-  if (last_cycle && Pike_interpreter.evaluator_stack) {
+  if (last_cycle && Pike_interpreter.evaluator_stack &&
+      !gc_destruct_everything) {
     objs -= num_objects;
     warn_bad_cycles();
     objs += num_objects;
@@ -3056,11 +3091,11 @@ size_t do_gc(void *ignored, int explicit_call)
 #ifdef PIKE_DEBUG
   if (gc_extra_refs) {
     size_t e;
-    struct marker *m;
     fprintf (stderr, "Lost track of %d extra refs to things in gc.\n"
 	     "Searching for marker(s) with extra refs:\n", gc_extra_refs);
-    for (e = 0; e < marker_hash_table_size; e++)
-      for (m = marker_hash_table[e]; m; m = m->next)
+    for (e = 0; e < marker_hash_table_size; e++) {
+      struct marker *s = marker_hash_table[e], *m;
+      for (m = s; m;) {
 	if (m->flags & GC_GOT_EXTRA_REF) {
 	  fprintf (stderr, "========================================\n"
 		   "Found marker with extra ref: ");
@@ -3068,6 +3103,14 @@ size_t do_gc(void *ignored, int explicit_call)
 	  fprintf (stderr, "Describing the thing pointed to:\n");
 	  describe (m->data);
 	}
+	m = m->next;
+	/* The marker might be moved to the head of the chain via
+	 * describe() above, so do this to avoid infinite recursion.
+	 * Some entries in the chain might be missed, but I don't want
+	 * to bother. */
+	if (m == s) break;
+      }
+    }
     fprintf (stderr, "========================================\n"
 	     "Done searching for marker(s) with extra refs.\n");
     Pike_fatal("Lost track of %d extra refs to things in gc.\n", gc_extra_refs);
diff --git a/src/gc.h b/src/gc.h
index 371748a8e61dd8bab5290bdbb2bfdfdcbe8a1789..f310185eb7c328ecc849abc04fc351ae8d9f36c4 100644
--- a/src/gc.h
+++ b/src/gc.h
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: gc.h,v 1.105 2003/09/23 20:06:28 mast Exp $
+|| $Id: gc.h,v 1.106 2004/03/15 22:47:15 mast Exp $
 */
 
 #ifndef GC_H
@@ -55,6 +55,9 @@ extern ptrdiff_t alloc_threshold;
 PMOD_EXPORT extern int Pike_in_gc;
 extern int gc_generation;
 extern int gc_trace, gc_debug;
+#ifdef DO_PIKE_CLEANUP
+extern int gc_destruct_everything;
+#endif
 extern cpu_time_t auto_gc_time;
 
 extern struct callback *gc_evaluator_callback;
diff --git a/src/main.c b/src/main.c
index 23375d98d9440d5bbff778b9a3cd28c5ec49f33b..c0820c467dad389d04e9eb07f1c6e7107fc7cbf5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,11 +2,11 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: main.c,v 1.191 2004/03/15 22:23:14 mast Exp $
+|| $Id: main.c,v 1.192 2004/03/15 22:47:15 mast Exp $
 */
 
 #include "global.h"
-RCSID("$Id: main.c,v 1.191 2004/03/15 22:23:14 mast Exp $");
+RCSID("$Id: main.c,v 1.192 2004/03/15 22:47:15 mast Exp $");
 #include "fdlib.h"
 #include "backend.h"
 #include "module.h"
@@ -906,8 +906,11 @@ void exit_main(void)
   /* Destruct all remaining objects before modules are shut down, so
    * that they don't get calls to object exit callbacks after their
    * module exit callback. The downside is that the leak report below
-   * will always report destructed objects. */
-  cleanup_objects();
+   * will always report destructed objects. We use the gc in a special
+   * mode for this to get a reasonably sane destruct order. */
+  gc_destruct_everything = 1;
+  do_gc (NULL, 1);
+  gc_destruct_everything = 0;
 
   /* Unload dynamic modules before static ones. */
   exit_dynamic_load();
diff --git a/src/object.c b/src/object.c
index 3aab923387c86482d5644f3acf81c3db2dd4318c..d8a9cef14227f6c4bd6e562c0a338ff58b626e2d 100644
--- a/src/object.c
+++ b/src/object.c
@@ -2,11 +2,11 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: object.c,v 1.250 2003/11/18 19:33:45 grubba Exp $
+|| $Id: object.c,v 1.251 2004/03/15 22:47:15 mast Exp $
 */
 
 #include "global.h"
-RCSID("$Id: object.c,v 1.250 2003/11/18 19:33:45 grubba Exp $");
+RCSID("$Id: object.c,v 1.251 2004/03/15 22:47:15 mast Exp $");
 #include "object.h"
 #include "dynamic_buffer.h"
 #include "interpret.h"
@@ -1601,28 +1601,6 @@ PMOD_EXPORT int object_equal_p(struct object *a, struct object *b, struct proces
   return 1;
 }
 
-void cleanup_objects(void)
-{
-  struct object *o, *next;
-
-  for(o=first_object;o;o=next)
-  {
-    add_ref(o);
-    if(o != master_object &&	/* Wait with the master till last. */
-       o->prog)
-    {
-      debug_malloc_touch(o);
-      debug_malloc_touch(o->storage);
-      call_destroy(o,1);
-      destruct(o);
-    } else {
-      debug_malloc_touch(o);
-    }
-    SET_NEXT_AND_FREE(o,free_object);
-  }
-  destruct_objects_to_destruct();
-}
-
 PMOD_EXPORT struct array *object_indices(struct object *o)
 {
   struct program *p;
diff --git a/src/object.h b/src/object.h
index 9f082b260639aee18ad2b5e3b8c403e4fdfbe35a..e6f97a67f1a3f2b6aec64a00e03c3725dd5b3a07 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: object.h,v 1.80 2003/08/01 22:46:06 mast Exp $
+|| $Id: object.h,v 1.81 2004/03/15 22:47:15 mast Exp $
 */
 
 #ifndef OBJECT_H
@@ -98,7 +98,6 @@ union anything *object_get_item_ptr(struct object *o,
 				    struct svalue *key,
 				    TYPE_T type);
 PMOD_EXPORT int object_equal_p(struct object *a, struct object *b, struct processing *p);
-void cleanup_objects(void);
 PMOD_EXPORT struct array *object_indices(struct object *o);
 PMOD_EXPORT struct array *object_values(struct object *o);
 PMOD_EXPORT void gc_mark_object_as_referenced(struct object *o);