diff --git a/src/array.c b/src/array.c
index 2c61ce8ccdb2d295d8ef5788e3c666219d9e869e..7e2afa65e93ed167fa4b894e34cd2586119c80d4 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.205 2008/05/02 04:15:09 mast Exp $
+|| $Id: array.c,v 1.206 2008/05/11 02:35:21 mast Exp $
 */
 
 #include "global.h"
@@ -2595,6 +2595,31 @@ void check_all_arrays(void)
 #endif /* PIKE_DEBUG */
 
 
+void visit_array (struct array *a, int action)
+{
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += sizeof (struct array) +
+	(a->malloced_size - 1) * sizeof (struct svalue);
+      break;
+  }
+
+  if (a->type_field &
+      (action & VISIT_COMPLEX_ONLY ? BIT_COMPLEX : BIT_REF_TYPES)) {
+    size_t e, s = a->size;
+    int ref_type = a->flags & ARRAY_WEAK_FLAG ? REF_TYPE_WEAK : REF_TYPE_NORMAL;
+    for (e = 0; e < s; e++)
+      visit_svalue (ITEM (a) + e, ref_type);
+  }
+}
+
 static void gc_check_array(struct array *a)
 {
   GC_ENTER (a, T_ARRAY) {
@@ -2614,12 +2639,6 @@ void gc_mark_array_as_referenced(struct array *a)
 {
   if(gc_mark(a))
     GC_ENTER (a, T_ARRAY) {
-      if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-	gc_counted_bytes += sizeof (struct array) +
-	  (a->malloced_size - 1) * sizeof (struct svalue);
-	gc_check_array (a);
-      }
-
       if (a == gc_mark_array_pos)
 	gc_mark_array_pos = a->next;
       if (a == gc_internal_array)
diff --git a/src/array.h b/src/array.h
index 46d277ae597294726c9309946357113599116556..745fc7726a8928a0dcfcf0db4fbddb8c8b5457d5 100644
--- a/src/array.h
+++ b/src/array.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: array.h,v 1.73 2008/05/01 21:44:32 mast Exp $
+|| $Id: array.h,v 1.74 2008/05/11 02:35:21 mast Exp $
 */
 
 #ifndef ARRAY_H
@@ -192,6 +192,7 @@ void array_replace(struct array *a,
 PMOD_EXPORT void check_array(struct array *a);
 void check_all_arrays(void);
 #endif
+void visit_array (struct array *a, int action);
 void gc_mark_array_as_referenced(struct array *a);
 void real_gc_cycle_check_array(struct array *a, int weak);
 unsigned gc_touch_all_arrays(void);
@@ -211,6 +212,9 @@ PMOD_EXPORT struct array *implode_array(struct array *a, struct array *b);
 
 #define array_get_flags(a) ((a)->flags)
 
+#define visit_array_ref(A, REF_TYPE)					\
+  visit_ref (pass_array (A), (REF_TYPE),				\
+	     (visit_thing_fn *) &visit_array, NULL)
 #define gc_cycle_check_array(X, WEAK) \
   gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_array, (X), (WEAK))
 
diff --git a/src/constants.c b/src/constants.c
index 72c862b927cbc375caf6041a8a0bbc9ada0e486c..811c8b04411e3db537d2c97683b7a0b4712b5d91 100644
--- a/src/constants.c
+++ b/src/constants.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: constants.c,v 1.58 2008/04/26 16:07:39 grubba Exp $
+|| $Id: constants.c,v 1.59 2008/05/11 02:35:22 mast Exp $
 */
 
 #include "global.h"
@@ -16,6 +16,8 @@
 #include "mapping.h"
 #include "pike_error.h"
 #include "pike_security.h"
+#include "gc.h"
+
 #include "block_alloc.h"
 
 struct mapping *builtin_constants = 0;
@@ -188,6 +190,30 @@ PMOD_EXPORT struct callable *quick_add_efun(const char *name, ptrdiff_t name_len
   return ret;
 }
 
+void visit_callable (struct callable *c, int action)
+{
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += sizeof (struct callable);
+      break;
+  }
+
+  if (!(action & VISIT_COMPLEX_ONLY)) {
+    visit_type_ref (c->type, REF_TYPE_NORMAL);
+    visit_string_ref (c->name, REF_TYPE_NORMAL);
+  }
+
+  /* Looks like the c->prog isn't refcounted..? */
+  /* visit_program_ref (c->prog, REF_TYPE_NORMAL); */
+}
+
 #ifdef PIKE_DEBUG
 void present_constant_profiling(void)
 {
diff --git a/src/constants.h b/src/constants.h
index ae5cb016d7e3b5eb52c5dbbf84d21f48db861ee7..f0c03be31ff55475e2da43917086321f0a7d7ab9 100644
--- a/src/constants.h
+++ b/src/constants.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: constants.h,v 1.35 2008/04/26 16:07:39 grubba Exp $
+|| $Id: constants.h,v 1.36 2008/05/11 02:35:22 mast Exp $
 */
 
 #ifndef ADD_EFUN_H
@@ -75,10 +75,14 @@ PMOD_EXPORT struct callable *quick_add_efun(const char *name, ptrdiff_t name_len
 					    int flags,
 					    optimize_fun optimize,
 					    docode_fun docode);
+void visit_callable (struct callable *c, int action);
 void init_builtin_constants(void);
 void exit_builtin_constants(void);
 /* Prototypes end here */
 
+#define visit_callable_ref(C, REF_TYPE)				\
+  visit_ref (pass_callable (C), (REF_TYPE),			\
+	     (visit_thing_fn *) &visit_callable, NULL)
 
 #include "pike_macros.h"
 
diff --git a/src/mapping.c b/src/mapping.c
index be825a4eb4a87ccb5964e8f8950206d298db128a..1b2a9178636668573c5c4448be2b3bddc17bd965 100644
--- a/src/mapping.c
+++ b/src/mapping.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: mapping.c,v 1.199 2008/05/02 04:15:11 mast Exp $
+|| $Id: mapping.c,v 1.200 2008/05/11 02:35:22 mast Exp $
 */
 
 #include "global.h"
@@ -277,7 +277,7 @@ PMOD_EXPORT void do_free_mapping(struct mapping *m)
     inl_free_mapping(m);
 }
 
-/* This function is used to rehash a mapping without loosing the internal
+/* This function is used to rehash a mapping without losing the internal
  * order in each hash chain. This is to prevent mappings from becoming
  * inefficient just after being rehashed.
  */
@@ -2316,14 +2316,62 @@ void check_all_mappings(void)
 }
 #endif
 
+static void visit_mapping_data (struct mapping_data *md, int action,
+				struct mapping *m)
+{
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += MAPPING_DATA_SIZE (md->hashsize, md->num_keypairs);
+      break;
+  }
+
+  if ((md->ind_types | md->val_types) &
+      (action & VISIT_COMPLEX_ONLY ? BIT_COMPLEX : BIT_REF_TYPES)) {
+    int ind_ref_type =
+      md->flags & MAPPING_WEAK_INDICES ? REF_TYPE_WEAK : REF_TYPE_NORMAL;
+    int val_ref_type =
+      md->flags & MAPPING_WEAK_VALUES ? REF_TYPE_WEAK : REF_TYPE_NORMAL;
+    INT32 e;
+    struct keypair *k;
+    NEW_MAPPING_LOOP (md) {
+      visit_svalue (&k->ind, ind_ref_type);
+      visit_svalue (&k->val, val_ref_type);
+    }
+  }
+}
+
+void visit_mapping (struct mapping *m, int action)
+{
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += sizeof (struct mapping);
+      break;
+  }
+
+  visit_ref (m->data, REF_TYPE_INTERNAL,
+	     (visit_thing_fn *) &visit_mapping_data, m);
+}
+
 #ifdef MAPPING_SIZE_DEBUG
 #define DO_IF_MAPPING_SIZE_DEBUG(x) x
 #else
 #define DO_IF_MAPPING_SIZE_DEBUG(x)
 #endif
 
-static void gc_check_md (struct mapping_data *md);
-
 #define GC_RECURSE_MD_IN_USE(MD, RECURSE_FN, IND_TYPES, VAL_TYPES) do { \
   INT32 e;								\
   struct keypair *k;							\
@@ -2338,7 +2386,7 @@ static void gc_check_md (struct mapping_data *md);
 } while (0)
 
 #ifdef PIKE_MAPPING_KEYPAIR_LOOP
-/* NOTE: Broken code below! */
+#error Broken code below!
 #define GC_RECURSE(M, MD, REC_KEYPAIR, TYPE, IND_TYPES, VAL_TYPES) do {	\
     int remove;								\
     struct keypair *k,**prev_;						\
@@ -2465,11 +2513,6 @@ void gc_mark_mapping_as_referenced(struct mapping *m)
     GC_ENTER (m, T_MAPPING) {
       struct mapping_data *md = m->data;
 
-      if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-	gc_counted_bytes += sizeof (struct mapping);
-	gc_check (md);
-      }
-
       if (m == gc_mark_mapping_pos)
 	gc_mark_mapping_pos = m->next;
       if (m == gc_internal_mapping)
@@ -2479,52 +2522,44 @@ void gc_mark_mapping_as_referenced(struct mapping *m)
 	DOUBLELINK(first_mapping, m); /* Linked in first. */
       }
 
-      if(gc_mark(md)) {
-	if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-	  gc_counted_bytes +=
-	    MAPPING_DATA_SIZE (md->hashsize, md->num_keypairs);
-	  gc_check_md (md);
+      if(gc_mark(md) && ((md->ind_types | md->val_types) & BIT_COMPLEX)) {
+	TYPE_FIELD ind_types = 0, val_types = 0;
+	if (MAPPING_DATA_IN_USE(md)) {
+	  /* Must leave the mapping data untouched if it's busy. */
+	  debug_malloc_touch(m);
+	  debug_malloc_touch(md);
+	  GC_RECURSE_MD_IN_USE(md, gc_mark_svalues, ind_types, val_types);
+	  gc_assert_checked_as_nonweak(md);
 	}
-
-	if ((md->ind_types | md->val_types) & BIT_COMPLEX) {
-	  TYPE_FIELD ind_types = 0, val_types = 0;
-	  if (MAPPING_DATA_IN_USE(md)) {
-	    /* Must leave the mapping data untouched if it's busy. */
-	    debug_malloc_touch(m);
-	    debug_malloc_touch(md);
-	    GC_RECURSE_MD_IN_USE(md, gc_mark_svalues, ind_types, val_types);
-	    gc_assert_checked_as_nonweak(md);
+	else
+	  switch (md->flags & MAPPING_WEAK) {
+	    case 0:
+	      debug_malloc_touch(m);
+	      debug_malloc_touch(md);
+	      GC_RECURSE(m, md, GC_REC_KP, gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_nonweak(md);
+	      break;
+	    case MAPPING_WEAK_INDICES:
+	      debug_malloc_touch(m);
+	      debug_malloc_touch(md);
+	      GC_RECURSE(m, md, GC_REC_KP_IND, gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_weak(md);
+	      break;
+	    case MAPPING_WEAK_VALUES:
+	      debug_malloc_touch(m);
+	      debug_malloc_touch(md);
+	      GC_RECURSE(m, md, GC_REC_KP_VAL, gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_weak(md);
+	      break;
+	    default:
+	      debug_malloc_touch(m);
+	      debug_malloc_touch(md);
+	      GC_RECURSE(m, md, GC_REC_KP_BOTH, gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_weak(md);
+	      break;
 	  }
-	  else
-	    switch (md->flags & MAPPING_WEAK) {
-	      case 0:
-		debug_malloc_touch(m);
-		debug_malloc_touch(md);
-		GC_RECURSE(m, md, GC_REC_KP, gc_mark, ind_types, val_types);
-		gc_assert_checked_as_nonweak(md);
-		break;
-	      case MAPPING_WEAK_INDICES:
-		debug_malloc_touch(m);
-		debug_malloc_touch(md);
-		GC_RECURSE(m, md, GC_REC_KP_IND, gc_mark, ind_types, val_types);
-		gc_assert_checked_as_weak(md);
-		break;
-	      case MAPPING_WEAK_VALUES:
-		debug_malloc_touch(m);
-		debug_malloc_touch(md);
-		GC_RECURSE(m, md, GC_REC_KP_VAL, gc_mark, ind_types, val_types);
-		gc_assert_checked_as_weak(md);
-		break;
-	      default:
-		debug_malloc_touch(m);
-		debug_malloc_touch(md);
-		GC_RECURSE(m, md, GC_REC_KP_BOTH, gc_mark, ind_types,val_types);
-		gc_assert_checked_as_weak(md);
-		break;
-	    }
-	  md->val_types = val_types;
-	  md->ind_types = ind_types;
-	}
+	md->val_types = val_types;
+	md->ind_types = ind_types;
       }
     } GC_LEAVE;
 }
@@ -2583,45 +2618,52 @@ void real_gc_cycle_check_mapping(struct mapping *m, int weak)
   } GC_CYCLE_LEAVE;
 }
 
-static void gc_check_md (struct mapping_data *md)
+static void gc_check_mapping(struct mapping *m)
 {
-  INT32 e;
-  struct keypair *k;
+  struct mapping_data *md = m->data;
 
-  if (!(md->flags & MAPPING_WEAK) || MAPPING_DATA_IN_USE(md))
-    /* Disregard the weak flag if the mapping data is busy; we must
-     * leave it untouched in that case anyway. */
-    NEW_MAPPING_LOOP(md)
-    {
-      debug_gc_check_svalues(&k->ind, 1, " as mapping index");
-      debug_gc_check_svalues(&k->val, 1, " as mapping value");
-    }
-  else {
-    switch (md->flags & MAPPING_WEAK) {
-      case MAPPING_WEAK_INDICES:
-	NEW_MAPPING_LOOP(md)
-	{
-	  debug_gc_check_weak_svalues(&k->ind, 1, " as mapping index");
-	  debug_gc_check_svalues(&k->val, 1, " as mapping value");
-	}
-	break;
-      case MAPPING_WEAK_VALUES:
-	NEW_MAPPING_LOOP(md)
-	{
-	  debug_gc_check_svalues(&k->ind, 1, " as mapping index");
-	  debug_gc_check_weak_svalues(&k->val, 1, " as mapping value");
-	}
-	break;
-      default:
-	NEW_MAPPING_LOOP(md)
-	{
-	  debug_gc_check_weak_svalues(&k->ind, 1, " as mapping index");
-	  debug_gc_check_weak_svalues(&k->val, 1, " as mapping value");
+  if((md->ind_types | md->val_types) & BIT_COMPLEX)
+    GC_ENTER (m, T_MAPPING) {
+      INT32 e;
+      struct keypair *k;
+
+      if(!debug_gc_check (md, " as mapping data block of a mapping")) {
+	if (!(md->flags & MAPPING_WEAK) || MAPPING_DATA_IN_USE(md))
+	  /* Disregard the weak flag if the mapping data is busy; we must
+	   * leave it untouched in that case anyway. */
+	  NEW_MAPPING_LOOP(md)
+	  {
+	    debug_gc_check_svalues(&k->ind, 1, " as mapping index");
+	    debug_gc_check_svalues(&k->val, 1, " as mapping value");
+	  }
+	else {
+	  switch (md->flags & MAPPING_WEAK) {
+	    case MAPPING_WEAK_INDICES:
+	      NEW_MAPPING_LOOP(md)
+	      {
+		debug_gc_check_weak_svalues(&k->ind, 1, " as mapping index");
+		debug_gc_check_svalues(&k->val, 1, " as mapping value");
+	      }
+	      break;
+	    case MAPPING_WEAK_VALUES:
+	      NEW_MAPPING_LOOP(md)
+	      {
+		debug_gc_check_svalues(&k->ind, 1, " as mapping index");
+		debug_gc_check_weak_svalues(&k->val, 1, " as mapping value");
+	      }
+	      break;
+	    default:
+	      NEW_MAPPING_LOOP(md)
+	      {
+		debug_gc_check_weak_svalues(&k->ind, 1, " as mapping index");
+		debug_gc_check_weak_svalues(&k->val, 1, " as mapping value");
+	      }
+	      break;
+	  }
+	  gc_checked_as_weak(md);
 	}
-	break;
-    }
-    gc_checked_as_weak(md);
-  }
+      }
+    } GC_LEAVE;
 }
 
 unsigned gc_touch_all_mappings(void)
@@ -2645,22 +2687,15 @@ void gc_check_all_mappings(void)
 
   for(m=first_mapping;m;m=m->next)
   {
-    struct mapping_data *md = m->data;
-
 #ifdef DEBUG_MALLOC
-    if (((int) PTR_TO_INT (md)) == 0x55555555) {
+    if (((int) PTR_TO_INT (m->data)) == 0x55555555) {
       fprintf(stderr, "** Zapped mapping in list of active mappings!\n");
       describe_something(m, T_MAPPING, 0,2,0, NULL);
       Pike_fatal("Zapped mapping in list of active mappings!\n");
     }
 #endif /* DEBUG_MALLOC */
 
-    if((md->ind_types | md->val_types) & BIT_COMPLEX)
-      GC_ENTER (m, T_MAPPING) {
-	if(!debug_gc_check (md, " as mapping data block of a mapping"))
-	  gc_check_md (md);
-      } GC_LEAVE;
-
+    gc_check_mapping(m);
 #ifdef PIKE_DEBUG
     if(d_flag > 1) check_mapping_type_fields(m);
 #endif
diff --git a/src/mapping.h b/src/mapping.h
index b3ee2e8d4b6348748ec1194640eeb4ad076b20ef..0f648ff9c82ecbcb3ed4a288225881dcae694f22 100644
--- a/src/mapping.h
+++ b/src/mapping.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: mapping.h,v 1.66 2008/01/05 17:58:54 nilsson Exp $
+|| $Id: mapping.h,v 1.67 2008/05/11 02:35:22 mast Exp $
 */
 
 #ifndef MAPPING_H
@@ -372,6 +372,7 @@ PMOD_EXPORT void mapping_search_no_free(struct svalue *to,
 void check_mapping(const struct mapping *m);
 void check_all_mappings(void);
 #endif
+void visit_mapping (struct mapping *m, int action);
 void gc_mark_mapping_as_referenced(struct mapping *m);
 void real_gc_cycle_check_mapping(struct mapping *m, int weak);
 unsigned gc_touch_all_mappings(void);
@@ -388,6 +389,9 @@ int mapping_is_constant(struct mapping *m,
 
 #define allocate_mapping(X) dmalloc_touch(struct mapping *,debug_allocate_mapping(X))
 
+#define visit_mapping_ref(M, REF_TYPE)				\
+  visit_ref (pass_mapping (M), (REF_TYPE),			\
+	     (visit_thing_fn *) &visit_mapping, NULL)
 #define gc_cycle_check_mapping(X, WEAK) \
   gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_mapping, (X), (WEAK))
 
diff --git a/src/multiset.c b/src/multiset.c
index fbb0dac45bc6d449a119f0b43f60e56762009b77..1e192cef81ddf331505ab4e88da2cf6a09b991dc 100644
--- a/src/multiset.c
+++ b/src/multiset.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: multiset.c,v 1.109 2008/05/06 19:19:10 mast Exp $
+|| $Id: multiset.c,v 1.110 2008/05/11 02:35:22 mast Exp $
 */
 
 #include "global.h"
@@ -3800,10 +3800,72 @@ PMOD_EXPORT ptrdiff_t multiset_get_nth (struct multiset *l, size_t n)
   return MSNODE2OFF (l->msd, RBNODE (rb_get_nth (HDR (l->msd->root), n)));
 }
 
+
 #define GC_MSD_VISITED GC_USER_1
 #define GC_MSD_GOT_EXT_REFS GC_USER_2
 #define GC_MSD_GOT_NODE_REFS GC_USER_3
 
+static void visit_multiset_data (struct multiset_data *msd, int action,
+				 struct multiset *l)
+{
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += (msd->flags & MULTISET_INDVAL ?
+			   NODE_OFFSET (msnode_indval, msd->allocsize) :
+			   NODE_OFFSET (msnode_ind, msd->allocsize));
+      break;
+  }
+
+  if (msd->root &&
+      ((msd->ind_types | msd->val_types) &
+       (action & VISIT_COMPLEX_ONLY ? BIT_COMPLEX : BIT_REF_TYPES))) {
+    int ind_ref_type =
+      msd->flags & MULTISET_WEAK_INDICES ? REF_TYPE_WEAK : REF_TYPE_NORMAL;
+    union msnode *node = low_multiset_first (msd);
+    struct svalue ind;
+    if (msd->flags & MULTISET_INDVAL) {
+      int val_ref_type =
+	msd->flags & MULTISET_WEAK_VALUES ? REF_TYPE_WEAK : REF_TYPE_NORMAL;
+      do {
+	low_use_multiset_index (node, ind);
+	visit_svalue (&ind, ind_ref_type);
+	visit_svalue (&node->iv.val, val_ref_type);
+      } while ((node = low_multiset_next (node)));
+    }
+    else
+      do {
+	low_use_multiset_index (node, ind);
+	visit_svalue (&ind, ind_ref_type);
+      } while ((node = low_multiset_next (node)));
+  }
+}
+
+void visit_multiset (struct multiset *l, int action)
+{
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += sizeof (struct multiset);
+      break;
+  }
+
+  visit_ref (l->msd, REF_TYPE_INTERNAL,
+	     (visit_thing_fn *) &visit_multiset_data, l);
+}
+
 unsigned gc_touch_all_multisets (void)
 {
   unsigned n = 0;
@@ -3819,8 +3881,6 @@ unsigned gc_touch_all_multisets (void)
   return n;
 }
 
-static void gc_check_msd (struct multiset *l);
-
 void gc_check_all_multisets (void)
 {
   struct multiset *l;
@@ -3849,76 +3909,69 @@ void gc_check_all_multisets (void)
     } GC_LEAVE;
   }
 
-  for (l = first_multiset; l; l = l->next)
-    gc_check_msd (l);
-}
-
-static void gc_check_msd (struct multiset *l)
-{
-  struct multiset_data *msd = l->msd;
-  struct marker *m = get_marker (msd);
-
-  /* m->refs doesn't have any useful value here when called for
-   * GC_PASS_COUNT_MEMORY. It doesn't matter since we're making no
-   * difference between normal and weak refs anyway in that mode. */
+  for (l = first_multiset; l; l = l->next) {
+    struct multiset_data *msd = l->msd;
+    struct marker *m = get_marker (msd);
 
-  if (!(m->flags & GC_MSD_VISITED))
-    GC_ENTER (l, T_MULTISET) {
-      if (m->refs < msd->refs) m->flags |= GC_MSD_GOT_EXT_REFS;
+    if (!(m->flags & GC_MSD_VISITED))
+      GC_ENTER (l, T_MULTISET) {
+	if (m->refs < msd->refs) m->flags |= GC_MSD_GOT_EXT_REFS;
 
-      if (msd->root) {
-	union msnode *node = low_multiset_first (msd);
-	struct svalue ind;
+	if (msd->root) {
+	  union msnode *node = low_multiset_first (msd);
+	  struct svalue ind;
 
 #define WITH_NODES_BLOCK(TYPE, OTHERTYPE, IND, INDVAL)			\
-	if (!(msd->flags & MULTISET_WEAK) || (m->flags & GC_MSD_GOT_EXT_REFS)) \
-	  do {								\
-	    low_use_multiset_index (node, ind);				\
-	    debug_gc_check_svalues (&ind, 1, " as multiset index");	\
-	    INDVAL (debug_gc_check_svalues (&node->iv.val, 1,		\
-					    " as multiset value"));	\
-	  } while ((node = low_multiset_next (node)));			\
+	  if (!(msd->flags & MULTISET_WEAK) ||				\
+	      (m->flags & GC_MSD_GOT_EXT_REFS))				\
+	    do {							\
+	      low_use_multiset_index (node, ind);			\
+	      debug_gc_check_svalues (&ind, 1, " as multiset index");	\
+	      INDVAL (debug_gc_check_svalues (&node->iv.val, 1,		\
+					      " as multiset value"));	\
+	    } while ((node = low_multiset_next (node)));		\
 									\
-	else {								\
-	  switch (msd->flags & MULTISET_WEAK) {				\
-	    case MULTISET_WEAK_INDICES:					\
-	      do {							\
-		low_use_multiset_index (node, ind);			\
-		debug_gc_check_weak_svalues (&ind, 1, " as multiset index"); \
-		INDVAL (debug_gc_check_svalues (&node->iv.val, 1,	\
-						" as multiset value")); \
-	      } while ((node = low_multiset_next (node)));		\
-	      break;							\
+	  else {							\
+	    switch (msd->flags & MULTISET_WEAK) {			\
+	      case MULTISET_WEAK_INDICES:				\
+		do {							\
+		  low_use_multiset_index (node, ind);			\
+		  debug_gc_check_weak_svalues (&ind, 1, " as multiset index"); \
+		  INDVAL (debug_gc_check_svalues (&node->iv.val, 1,	\
+						  " as multiset value")); \
+		} while ((node = low_multiset_next (node)));		\
+		break;							\
 									\
-	    case MULTISET_WEAK_VALUES:					\
-	      do {							\
-		low_use_multiset_index (node, ind);			\
-		debug_gc_check_svalues (&ind, 1, " as multiset index"); \
-		INDVAL (debug_gc_check_weak_svalues (&node->iv.val, 1,	\
-						     " as multiset value")); \
-	      } while ((node = low_multiset_next (node)));		\
-	      break;							\
+	      case MULTISET_WEAK_VALUES:				\
+		do {							\
+		  low_use_multiset_index (node, ind);			\
+		  debug_gc_check_svalues (&ind, 1, " as multiset index"); \
+		  INDVAL (debug_gc_check_weak_svalues (&node->iv.val, 1, \
+						       " as multiset value")); \
+		} while ((node = low_multiset_next (node)));		\
+		break;							\
 									\
-	    default:							\
-	      do {							\
-		low_use_multiset_index (node, ind);			\
-		debug_gc_check_weak_svalues (&ind, 1, " as multiset index"); \
-		INDVAL (debug_gc_check_weak_svalues (&node->iv.val, 1,	\
-						     " as multiset value")); \
-	      } while ((node = low_multiset_next (node)));		\
-	      break;							\
-	  }								\
-	  gc_checked_as_weak (msd);					\
-	}
+	      default:							\
+		do {							\
+		  low_use_multiset_index (node, ind);			\
+		  debug_gc_check_weak_svalues (&ind, 1, " as multiset index"); \
+		  INDVAL (debug_gc_check_weak_svalues (&node->iv.val, 1, \
+						       " as multiset value")); \
+		} while ((node = low_multiset_next (node)));		\
+		break;							\
+	    }								\
+	    gc_checked_as_weak (msd);					\
+	  }
 
-	DO_WITH_NODES (msd);
+	  DO_WITH_NODES (msd);
 
 #undef WITH_NODES_BLOCK
-      }
+	}
 
-      if (l->node_refs) m->flags |= GC_MSD_GOT_NODE_REFS | GC_MSD_VISITED;
-      else m->flags |= GC_MSD_VISITED;
-    } GC_LEAVE;
+	if (l->node_refs) m->flags |= GC_MSD_GOT_NODE_REFS | GC_MSD_VISITED;
+	else m->flags |= GC_MSD_VISITED;
+      } GC_LEAVE;
+  }
 }
 
 static void gc_unlink_msnode_shared (struct multiset_data *msd,
@@ -4081,11 +4134,6 @@ void gc_mark_multiset_as_referenced (struct multiset *l)
     GC_ENTER (l, T_MULTISET) {
       struct multiset_data *msd = l->msd;
 
-      if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-	gc_counted_bytes += sizeof (struct multiset);
-	gc_check (msd);
-      }
-
       if (l == gc_mark_multiset_pos)
 	gc_mark_multiset_pos = l->next;
       if (l == gc_internal_multiset)
@@ -4095,67 +4143,59 @@ void gc_mark_multiset_as_referenced (struct multiset *l)
 	DOUBLELINK (first_multiset, l); /* Linked in first. */
       }
 
-      if (gc_mark (msd)) {
-	if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-	  gc_counted_bytes += (msd->flags & MULTISET_INDVAL ?
-			       NODE_OFFSET (msnode_indval, msd->allocsize) :
-			       NODE_OFFSET (msnode_ind, msd->allocsize));
-	  gc_check_msd (l);
-	}
+      if (gc_mark (msd) && msd->root &&
+	  ((msd->ind_types | msd->val_types) & BIT_COMPLEX)) {
+	struct marker *m = get_marker (msd);
+	TYPE_FIELD ind_types = 0, val_types = 0;
 
-	if (msd->root && ((msd->ind_types | msd->val_types) & BIT_COMPLEX)) {
-	  struct marker *m = get_marker (msd);
-	  TYPE_FIELD ind_types = 0, val_types = 0;
+	if (m->flags & GC_MSD_GOT_EXT_REFS) {
+	  /* Must leave the multiset data untouched if there are direct
+	   * external refs to it. */
+	  GC_RECURSE_MSD_IN_USE (msd, gc_mark_svalues, ind_types, val_types);
+	  gc_assert_checked_as_nonweak (msd);
+	}
 
-	  if (m->flags & GC_MSD_GOT_EXT_REFS) {
-	    /* Must leave the multiset data untouched if there are direct
-	     * external refs to it. */
-	    GC_RECURSE_MSD_IN_USE (msd, gc_mark_svalues, ind_types, val_types);
-	    gc_assert_checked_as_nonweak (msd);
+	else {
+	  switch (msd->flags & MULTISET_WEAK) {
+	    case 0:
+	      GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
+			  GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_NONE,
+			  gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_nonweak (msd);
+	      break;
+	    case MULTISET_WEAK_INDICES:
+	      GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
+			  GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_IND,
+			  gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_weak (msd);
+	      break;
+	    case MULTISET_WEAK_VALUES:
+	      GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
+			  GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_VAL,
+			  gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_weak (msd);
+	      break;
+	    default:
+	      GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
+			  GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_BOTH,
+			  gc_mark, ind_types, val_types);
+	      gc_assert_checked_as_weak (msd);
+	      break;
 	  }
 
-	  else {
-	    switch (msd->flags & MULTISET_WEAK) {
-	      case 0:
-		GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
-			    GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_NONE,
-			    gc_mark, ind_types, val_types);
-		gc_assert_checked_as_nonweak (msd);
-		break;
-	      case MULTISET_WEAK_INDICES:
-		GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
-			    GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_IND,
-			    gc_mark, ind_types, val_types);
-		gc_assert_checked_as_weak (msd);
-		break;
-	      case MULTISET_WEAK_VALUES:
-		GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
-			    GC_REC_I_WEAK_NONE, GC_REC_IV_WEAK_VAL,
-			    gc_mark, ind_types, val_types);
-		gc_assert_checked_as_weak (msd);
-		break;
-	      default:
-		GC_RECURSE (msd, m->flags & GC_MSD_GOT_NODE_REFS,
-			    GC_REC_I_WEAK_IND, GC_REC_IV_WEAK_BOTH,
-			    gc_mark, ind_types, val_types);
-		gc_assert_checked_as_weak (msd);
-		break;
-	    }
-
-	    if (msd->refs == 1 && DO_SHRINK (msd, 0)) {
-	      /* Only shrink the multiset if it isn't shared, or else we
-	       * can end up with larger memory consumption since the
-	       * shrunk data blocks won't be shared. */
-	      debug_malloc_touch (msd);
-	      l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size), 0);
-	      debug_malloc_touch (l->msd);
-	      msd = l->msd;
-	    }
+	  if (msd->refs == 1 && DO_SHRINK (msd, 0)) {
+	    /* Only shrink the multiset if it isn't shared, or else we
+	     * can end up with larger memory consumption since the
+	     * shrunk data blocks won't be shared. */
+	    debug_malloc_touch (msd);
+	    l->msd = resize_multiset_data (msd, ALLOC_SIZE (msd->size), 0);
+	    debug_malloc_touch (l->msd);
+	    msd = l->msd;
 	  }
-
-	  msd->ind_types = ind_types;
-	  if (msd->flags & MULTISET_INDVAL) msd->val_types = val_types;
 	}
+
+	msd->ind_types = ind_types;
+	if (msd->flags & MULTISET_INDVAL) msd->val_types = val_types;
       }
     } GC_LEAVE;
 }
diff --git a/src/multiset.h b/src/multiset.h
index 8080489acd60005f80683e238c6d24ea4797a814..71ac914e9c2727080884aa5727b4e535548cfd39 100644
--- a/src/multiset.h
+++ b/src/multiset.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: multiset.h,v 1.42 2008/05/01 21:44:33 mast Exp $
+|| $Id: multiset.h,v 1.43 2008/05/11 02:35:22 mast Exp $
 */
 
 #ifndef MULTISET_H
@@ -429,6 +429,7 @@ struct multiset *copy_multiset_recursively (struct multiset *l,
 					    struct mapping *p);
 PMOD_EXPORT ptrdiff_t multiset_get_nth (struct multiset *l, size_t n);
 
+void visit_multiset (struct multiset *l, int action);
 unsigned gc_touch_all_multisets (void);
 void gc_check_all_multisets (void);
 void gc_mark_multiset_as_referenced (struct multiset *l);
@@ -437,6 +438,10 @@ void gc_zap_ext_weak_refs_in_multisets (void);
 void real_gc_cycle_check_multiset (struct multiset *l, int weak);
 void gc_cycle_check_all_multisets (void);
 size_t gc_free_all_unreferenced_multisets (void);
+
+#define visit_multiset_ref(L, REF_TYPE)				\
+  visit_ref (pass_multiset (L), (REF_TYPE),			\
+	     (visit_thing_fn *) &visit_multiset, NULL)
 #define gc_cycle_check_multiset(X, WEAK) \
   gc_cycle_enqueue ((gc_cycle_check_cb *) real_gc_cycle_check_multiset, (X), (WEAK))
 
diff --git a/src/object.c b/src/object.c
index 7a7eca2f1421ebd3f51d5ae8498d68453f06ca1e..1b74da3f844d4f0018715e8631791b6781c7f283 100644
--- a/src/object.c
+++ b/src/object.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: object.c,v 1.289 2008/05/03 13:08:05 mast Exp $
+|| $Id: object.c,v 1.290 2008/05/11 02:35:22 mast Exp $
 */
 
 #include "global.h"
@@ -27,6 +27,7 @@
 #include "module_support.h"
 #include "fdlib.h"
 #include "mapping.h"
+#include "multiset.h"
 #include "constants.h"
 #include "encode.h"
 #include "pike_types.h"
@@ -1820,6 +1821,151 @@ PMOD_EXPORT struct array *object_values(struct object *o)
   return a;
 }
 
+
+void visit_object (struct object *o, int action)
+{
+  struct program *p = o->prog;
+
+  if (o->next == o) return; /* Fake object used by compiler */
+
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += sizeof (struct object);
+      if (p) mc_counted_bytes += p->storage_needed;
+      break;
+  }
+
+  if (PIKE_OBJ_INITED (o)) {
+    struct pike_frame *pike_frame = NULL;
+    struct inherit *inh = p->inherits;
+    char *storage = o->storage;
+    int e;
+
+    debug_malloc_touch (p);
+    debug_malloc_touch (o);
+    debug_malloc_touch (storage);
+
+    visit_program_ref (p, REF_TYPE_NORMAL);
+
+    for (e = p->num_inherits - 1; e >= 0; e--) {
+      struct program *inh_prog = inh[e].prog;
+      unsigned INT16 *inh_prog_var_idxs = inh_prog->variable_index;
+      struct identifier *inh_prog_ids = inh_prog->identifiers;
+      char *inh_storage = storage + inh[e].storage_offset;
+
+      int q, num_vars = (int) inh_prog->num_variable_index;
+
+      for (q = 0; q < num_vars; q++) {
+	int d = inh_prog_var_idxs[q];
+	struct identifier *id = inh_prog_ids + d;
+	int id_flags = id->identifier_flags;
+	int rtt = id->run_time_type;
+	void *var;
+	union anything *u;
+
+	if (IDENTIFIER_IS_ALIAS (id_flags))
+	  continue;
+
+	var = inh_storage + id->func.offset;
+	u = (union anything *) var;
+#ifdef DEBUG_MALLOC
+	if (rtt <= MAX_REF_TYPE)
+	  debug_malloc_touch (u->ptr);
+#endif
+
+	switch (rtt) {
+	  case T_MIXED: {
+	    struct svalue *s = (struct svalue *) var;
+	    dmalloc_touch_svalue (s);
+	    if ((s->type != T_OBJECT && s->type != T_FUNCTION) ||
+		s->u.object != o ||
+		!(id_flags & IDENTIFIER_NO_THIS_REF))
+	      visit_svalue (s, REF_TYPE_NORMAL);
+	    break;
+	  }
+
+	  case T_ARRAY:
+	    if (u->array)
+	      visit_array_ref (u->array, REF_TYPE_NORMAL);
+	    break;
+	  case T_MAPPING:
+	    if (u->mapping)
+	      visit_mapping_ref (u->mapping, REF_TYPE_NORMAL);
+	    break;
+	  case T_MULTISET:
+	    if (u->multiset)
+	      visit_multiset_ref (u->multiset, REF_TYPE_NORMAL);
+	    break;
+	  case T_PROGRAM:
+	    if (u->program)
+	      visit_program_ref (u->program, REF_TYPE_NORMAL);
+	    break;
+
+	  case T_OBJECT:
+	    if (u->object && (u->object != o ||
+			      !(id_flags & IDENTIFIER_NO_THIS_REF)))
+	      visit_object_ref (u->object, REF_TYPE_NORMAL);
+	    break;
+
+	  case T_STRING:
+	    if (u->string && !(action & VISIT_COMPLEX_ONLY))
+	      visit_string_ref (u->string, REF_TYPE_NORMAL);
+	    break;
+	  case T_TYPE:
+	    if (u->type && !(action & VISIT_COMPLEX_ONLY))
+	      visit_type_ref (u->type, REF_TYPE_NORMAL);
+	    break;
+
+#ifdef PIKE_DEBUG
+	  case PIKE_T_GET_SET:
+	  case T_INT:
+	  case T_FLOAT:
+	    break;
+	  default:
+	    Pike_fatal ("Invalid runtime type %d.\n", rtt);
+#endif
+	}
+      }
+
+      if (inh_prog->event_handler) {
+	if (!pike_frame) PUSH_FRAME2 (o, p);
+	SET_FRAME_CONTEXT (inh + e);
+	inh_prog->event_handler (PROG_EVENT_GC_RECURSE);
+      }
+    }
+
+    if (pike_frame) POP_FRAME2();
+
+    /* Strong ref follows. It must be last. */
+    if (p->flags & PROGRAM_USES_PARENT)
+      if (PARENT_INFO (o)->parent)
+	visit_object_ref (PARENT_INFO (o)->parent, REF_TYPE_STRONG);
+  }
+}
+
+PMOD_EXPORT void visit_function (struct svalue *s, int ref_type)
+{
+#ifdef PIKE_DEBUG
+  if (s->type != T_FUNCTION)
+    Pike_fatal ("Should only be called for a function svalue.\n");
+#endif
+
+  if (s->subtype == FUNCTION_BUILTIN)
+    /* Could avoid this if we had access to the action from the caller
+     * and check if it's VISIT_COMPLEX_ONLY. However, visit_callable
+     * will only return first thing. */
+    visit_callable_ref (s->u.efun, ref_type);
+  else
+    visit_object_ref (s->u.object, ref_type);
+}
+
 static void gc_check_object(struct object *o);
 
 PMOD_EXPORT void gc_mark_object_as_referenced(struct object *o)
@@ -1834,11 +1980,6 @@ PMOD_EXPORT void gc_mark_object_as_referenced(struct object *o)
       int e;
       struct program *p = o->prog;
 
-      if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-	if (p) gc_counted_bytes += p->storage_needed + sizeof (struct object);
-	gc_check_object (o);
-      }
-
       if (o == gc_mark_object_pos)
 	gc_mark_object_pos = o->next;
       if (o == gc_internal_object)
diff --git a/src/object.h b/src/object.h
index 2692a32cffc355aecf575fdda14ab4594b7da46e..ec8b589e29bf369b63c5f1f1333af4e966252a1d 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.94 2008/04/18 19:45:42 grubba Exp $
+|| $Id: object.h,v 1.95 2008/05/11 02:35:22 mast Exp $
 */
 
 #ifndef OBJECT_H
@@ -123,6 +123,8 @@ union anything *object_get_item_ptr(struct object *o,
 PMOD_EXPORT int object_equal_p(struct object *a, struct object *b, struct processing *p);
 PMOD_EXPORT struct array *object_indices(struct object *o);
 PMOD_EXPORT struct array *object_values(struct object *o);
+void visit_object (struct object *o, int action);
+PMOD_EXPORT void visit_function (struct svalue *s, int ref_type);
 PMOD_EXPORT void gc_mark_object_as_referenced(struct object *o);
 PMOD_EXPORT void real_gc_cycle_check_object(struct object *o, int weak);
 unsigned gc_touch_all_objects(void);
@@ -155,6 +157,9 @@ void check_all_objects(void);
 #define master() debug_master()
 #endif
 
+#define visit_object_ref(O, REF_TYPE)				\
+  visit_ref (pass_object (O), (REF_TYPE),			\
+	     (visit_thing_fn *) &visit_object, NULL)
 #define gc_cycle_check_object(X, WEAK) \
   gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_object, (X), (WEAK))
 
diff --git a/src/pike_types.c b/src/pike_types.c
index d082eb690ec56afa0af0678258cbdfe8f1f5cf9f..6a6c2eed88dcd8eb52f82abfdd9ba2d2a80b89c8 100644
--- a/src/pike_types.c
+++ b/src/pike_types.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: pike_types.c,v 1.330 2008/05/10 11:53:41 grubba Exp $
+|| $Id: pike_types.c,v 1.331 2008/05/11 02:35:22 mast Exp $
 */
 
 #include "global.h"
@@ -7837,77 +7837,85 @@ void cleanup_pike_type_table(void)
 #endif /* DO_PIKE_CLEANUP */
 }
 
-/* This is only enough gc stuff to detect leaking pike_type structs
- * and to get _locate_references and Pike.count_memory working. More
- * is needed if types are extended to contain pointers to other memory
- * objects or if they might contain cycles. */
-
-void gc_check_type (struct pike_type *t)
+void visit_type (struct pike_type *t, int action)
 {
-  debug_malloc_touch (t);
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+      break;
+#endif
+    case VISIT_COMPLEX_ONLY:
+      return;
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += sizeof (struct pike_type);
+      break;
+  }
 
-  GC_ENTER (t, T_TYPE) {
-    switch (t->type) {
-      case T_FUNCTION:
-      case T_MANY:
-      case T_TUPLE:
-      case T_MAPPING:
-      case T_OR:
-      case T_AND:
-      case PIKE_T_RING:
-      case PIKE_T_ATTRIBUTE:
-      case PIKE_T_NAME:
-	debug_gc_check (t->car, " as car in a type");
-	debug_gc_check (t->cdr, " as cdr in a type");
-	break;
-      case T_ARRAY:
-      case T_MULTISET:
-      case T_NOT:
-      case T_TYPE:
-      case T_PROGRAM:
-      case T_STRING:
-	debug_gc_check (t->car, " as car in a type");
-	break;
-      case T_SCOPE:
-      case T_ASSIGN:
-	debug_gc_check (t->cdr, " as cdr in a type");
-	break;
+  switch (t->type) {
+    case T_FUNCTION:
+    case T_MANY:
+    case T_TUPLE:
+    case T_MAPPING:
+    case T_OR:
+    case T_AND:
+    case PIKE_T_RING:
+      visit_type_ref (t->car, REF_TYPE_INTERNAL);
+      /* FALL_THROUGH */
+    case T_SCOPE:
+    case T_ASSIGN:
+      visit_type_ref (t->cdr, REF_TYPE_INTERNAL);
+      break;
+    case T_ARRAY:
+    case T_MULTISET:
+    case T_NOT:
+    case T_TYPE:
+    case T_PROGRAM:
+    case T_STRING:
+      visit_type_ref (t->car, REF_TYPE_INTERNAL);
+      break;
+    case PIKE_T_ATTRIBUTE:
+    case PIKE_T_NAME:
+      visit_string_ref ((struct pike_string *) t->car, REF_TYPE_INTERNAL);
+      visit_type_ref (t->cdr, REF_TYPE_INTERNAL);
+      break;
 #ifdef PIKE_DEBUG
-      case '0':
-      case '1':
-      case '2':
-      case '3':
-      case '4':
-      case '5':
-      case '6':
-      case '7':
-      case '8':
-      case '9':
-      case T_FLOAT:
-      case T_MIXED:
-      case T_VOID:
-      case T_ZERO:
-      case PIKE_T_UNKNOWN:
-      case T_INT:
-      case T_OBJECT:
-	break;
-      default:
-	Pike_fatal("gc_check_type: "
-		   "Unhandled type-node: %d\n", t->type);
-	break;
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+    case T_FLOAT:
+    case T_MIXED:
+    case T_VOID:
+    case T_ZERO:
+    case PIKE_T_UNKNOWN:
+    case T_INT:
+    case T_OBJECT:
+      break;
+    default:
+      Pike_fatal("visit_type: Unhandled type-node: %d\n", t->type);
+      break;
 #endif /* PIKE_DEBUG */
-    }
-  } GC_LEAVE;
+  }
 }
 
+#if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)
+
+/* This is only enough gc stuff to detect leaking pike_type structs
+ * and to locate references to them. More is needed if types are
+ * extended to contain pointers to other memory objects or if they
+ * might contain cycles. */
+
 void gc_mark_type_as_referenced(struct pike_type *t)
 {
   if (gc_mark(t)) {
-    if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-      gc_counted_bytes += sizeof (struct pike_type);
-      gc_check_type (t);
-    }
-
     GC_ENTER(t, PIKE_T_TYPE) {
       switch(t->type) {
       case PIKE_T_SCOPE:
@@ -7937,8 +7945,6 @@ void gc_mark_type_as_referenced(struct pike_type *t)
   }
 }
 
-#if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)
-
 #ifdef PIKE_DEBUG
 static void gc_mark_external_types(struct callback *cb, void *a, void *b)
 {
@@ -7995,6 +8001,64 @@ static void gc_mark_external_types(struct callback *cb, void *a, void *b)
 }
 #endif
 
+void gc_check_type (struct pike_type *t)
+{
+  debug_malloc_touch (t);
+
+  GC_ENTER (t, T_TYPE) {
+    switch (t->type) {
+      case T_FUNCTION:
+      case T_MANY:
+      case T_TUPLE:
+      case T_MAPPING:
+      case T_OR:
+      case T_AND:
+      case PIKE_T_RING:
+      case PIKE_T_ATTRIBUTE:
+      case PIKE_T_NAME:
+	debug_gc_check (t->car, " as car in a type");
+	debug_gc_check (t->cdr, " as cdr in a type");
+	break;
+      case T_ARRAY:
+      case T_MULTISET:
+      case T_NOT:
+      case T_TYPE:
+      case T_PROGRAM:
+      case T_STRING:
+	debug_gc_check (t->car, " as car in a type");
+	break;
+      case T_SCOPE:
+      case T_ASSIGN:
+	debug_gc_check (t->cdr, " as cdr in a type");
+	break;
+#ifdef PIKE_DEBUG
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+      case T_FLOAT:
+      case T_MIXED:
+      case T_VOID:
+      case T_ZERO:
+      case PIKE_T_UNKNOWN:
+      case T_INT:
+      case T_OBJECT:
+	break;
+      default:
+	Pike_fatal("gc_check_type: "
+		   "Unhandled type-node: %d\n", t->type);
+	break;
+#endif /* PIKE_DEBUG */
+    }
+  } GC_LEAVE;
+}
+
 void gc_check_all_types (void)
 {
   unsigned INT32 e;
diff --git a/src/pike_types.h b/src/pike_types.h
index 2023d5785fe6c70894ca1db5168ca1cc57adb339..74663359de977d812f2816cb2581344d42fbc7c9 100644
--- a/src/pike_types.h
+++ b/src/pike_types.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: pike_types.h,v 1.113 2008/04/25 13:45:22 grubba Exp $
+|| $Id: pike_types.h,v 1.114 2008/05/11 02:35:22 mast Exp $
 */
 
 #ifndef PIKE_TYPES_H
@@ -275,6 +275,7 @@ struct pike_type *object_type_to_program_type(struct pike_type *obj_t);
 PMOD_EXPORT char *get_name_of_type(TYPE_T t);
 void cleanup_pike_types(void);
 void cleanup_pike_type_table(void);
+void visit_type (struct pike_type *t, int action);
 void gc_mark_type_as_referenced(struct pike_type *t);
 void gc_check_type (struct pike_type *t);
 void gc_check_all_types (void);
@@ -289,6 +290,10 @@ void register_attribute_handler(struct pike_string *attr,
 				struct svalue *handler);
 /* Prototypes end here */
 
+#define visit_type_ref(T, REF_TYPE)				\
+  visit_ref (pass_type (T), (REF_TYPE),				\
+	     (visit_thing_fn *) &visit_type, NULL)
+
 #if 0 /* FIXME: Not supported under USE_PIKE_TYPE yet. */
 /* "Dynamic types" - use with ADD_FUNCTION_DTYPE */
 #define dtStore(TYPE) {int e; for (e=0; e<CONSTANT_STRLEN(TYPE); e++) unsafe_push_type((TYPE)[e]);}
diff --git a/src/program.c b/src/program.c
index fb98ccd946c72a7fb0ed5417b4115d2a9030f52b..406a25a4877837605cc49890fccc5ec8e5500412 100644
--- a/src/program.c
+++ b/src/program.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: program.c,v 1.690 2008/05/10 20:43:25 mast Exp $
+|| $Id: program.c,v 1.691 2008/05/11 02:35:22 mast Exp $
 */
 
 #include "global.h"
@@ -3758,17 +3758,16 @@ PMOD_EXPORT void set_exit_callback(void (*exit)(struct object *))
  * The callback is called after any mapped variables on the object
  * have been recursed (and possibly freed).
  *
- * If there are unmapped pointers to allocated memory then you should
- * add something like this to the recurse callback so that
- * Pike.count_memory remains accurate:
+ * If there are pointers to allocated memory that you keep track of on
+ * the C level then you should add something like this to the recurse
+ * callback so that Pike.count_memory remains accurate:
  *
- *   if (Pike_in_gc == GC_PASS_COUNT_MEMORY)
- *     gc_counted_bytes += <size of the allocated memory block(s)>
+ *   if (mc_count_bytes (Pike_fp->current_storage))
+ *     mc_counted_bytes += <size of the allocated memory block(s)>
  *
  * If the allocated memory is shared between objects then it gets more
- * complicated and you should insert calls to the gc check callback
- * too. See e.g. multiset.c or mapping.c for how to handle that
- * correctly.
+ * complicated and you need to write visit_thing_fn callbacks. See
+ * e.g. visit_mapping and visit_mapping_data for how to do that.
  *
  * This function is obsolete; see pike_set_prog_event_callback for
  * details.
@@ -9416,6 +9415,63 @@ void cleanup_program(void)
   }
 }
 
+
+void visit_program (struct program *p, int action)
+{
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += p->total_size;
+      break;
+  }
+
+  if (!(p->flags & PROGRAM_AVOID_CHECK)) {
+    int e;
+    struct program_constant *consts = p->constants;
+    struct inherit *inh = p->inherits;
+
+    for (e = p->num_constants - 1; e >= 0; e--)
+      visit_svalue (&consts[e].sval, REF_TYPE_NORMAL);
+
+    for (e = p->num_inherits - 1; e >= 0; e--) {
+      if (inh[e].parent)
+	visit_object_ref (inh[e].parent, REF_TYPE_NORMAL);
+
+      if (e && inh[e].prog)
+	visit_program_ref (inh[e].prog, REF_TYPE_NORMAL);
+    }
+
+    if (!(action & VISIT_COMPLEX_ONLY)) {
+      struct identifier *ids = p->identifiers;
+      struct pike_string **strs = p->strings;
+
+      for (e = p->num_inherits - 1; e >= 0; e--) {
+	if (inh[e].name)
+	  visit_string_ref (inh[e].name, REF_TYPE_NORMAL);
+      }
+
+      for (e = p->num_identifiers - 1; e >= 0; e--) {
+	struct identifier *id = ids + e;
+	visit_string_ref (id->name, REF_TYPE_NORMAL);
+	visit_type_ref (id->type, REF_TYPE_NORMAL);
+      }
+
+      for (e = p->num_strings - 1; e >= 0; e--)
+	visit_string_ref (strs[e], REF_TYPE_NORMAL);
+    }
+
+    /* Strong ref follows. It must be last. */
+    if (p->parent)
+      visit_program_ref (p->parent, REF_TYPE_STRONG);
+  }
+}
+
 static void gc_check_program(struct program *p);
 
 void gc_mark_program_as_referenced(struct program *p)
@@ -9446,11 +9502,6 @@ void gc_mark_program_as_referenced(struct program *p)
     GC_ENTER (p, T_PROGRAM) {
       int e;
 
-      if (Pike_in_gc == GC_PASS_COUNT_MEMORY) {
-	gc_counted_bytes += p->total_size;
-	gc_check_program (p);
-      }
-
       if (p == gc_mark_program_pos)
 	gc_mark_program_pos = p->next;
       if (p == gc_internal_program)
diff --git a/src/program.h b/src/program.h
index 82c0d537b6a3b166f78af9b8383d4c36fb56b9ca..52c7dda909f62999d272058156d31c988f66989a 100644
--- a/src/program.h
+++ b/src/program.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: program.h,v 1.241 2008/05/01 21:44:33 mast Exp $
+|| $Id: program.h,v 1.242 2008/05/11 02:35:23 mast Exp $
 */
 
 #ifndef PROGRAM_H
@@ -905,6 +905,7 @@ void check_all_programs(void);
 void placeholder_index(INT32 args);
 void init_program(void);
 void cleanup_program(void);
+void visit_program (struct program *p, int action);
 void gc_mark_program_as_referenced(struct program *p);
 void real_gc_cycle_check_program(struct program *p, int weak);
 unsigned gc_touch_all_programs(void);
@@ -1009,6 +1010,9 @@ void count_memory_in_programs(size_t *, size_t *);
 
 #define start_new_program() debug_start_new_program(__LINE__,__FILE__)
 
+#define visit_program_ref(P, REF_TYPE)				\
+  visit_ref (pass_program (P), (REF_TYPE),			\
+	     (visit_thing_fn *) &visit_program, NULL)
 #define gc_cycle_check_program(X, WEAK) \
   gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_program, (X), (WEAK))
 
diff --git a/src/stralloc.c b/src/stralloc.c
index 8cd3c9c4cfaceabf2d86fe7892cd96824bf01a0e..497a1692c55288a727e25d4b605b12f96500e773 100644
--- a/src/stralloc.c
+++ b/src/stralloc.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: stralloc.c,v 1.217 2008/05/03 15:29:26 nilsson Exp $
+|| $Id: stralloc.c,v 1.218 2008/05/11 02:36:00 mast Exp $
 */
 
 #include "global.h"
@@ -2134,11 +2134,20 @@ void count_memory_in_strings(size_t *num, size_t *size)
   size[0]=size_;
 }
 
-void gc_mark_string_as_referenced (struct pike_string *s)
+void visit_string (struct pike_string *s, int action)
 {
-  if (gc_mark (s))
-    if (Pike_in_gc == GC_PASS_COUNT_MEMORY)
-      gc_counted_bytes += memory_in_string (s);
+  switch (action) {
+#ifdef PIKE_DEBUG
+    default:
+      Pike_fatal ("Unknown visit action %d.\n", action);
+    case VISIT_NORMAL:
+    case VISIT_COMPLEX_ONLY:
+      break;
+#endif
+    case VISIT_COUNT_BYTES:
+      mc_counted_bytes += memory_in_string (s);
+      break;
+  }
 }
 
 #ifdef PIKE_DEBUG
@@ -2162,8 +2171,7 @@ void gc_mark_all_strings(void)
   for(e=0;e<htable_size;e++)
   {
     struct pike_string *p;
-    for(p=base_table[e];p;p=p->next)
-      gc_mark_string_as_referenced(p);
+    for(p=base_table[e];p;p=p->next) gc_is_referenced(p);
   }
 }
 #endif
diff --git a/src/stralloc.h b/src/stralloc.h
index 6bb771158bd09c9f647e10922c81b43423b99157..36e638a71ae041a922c363bc7b88599add22e94e 100644
--- a/src/stralloc.h
+++ b/src/stralloc.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: stralloc.h,v 1.102 2008/05/02 04:15:18 mast Exp $
+|| $Id: stralloc.h,v 1.103 2008/05/11 02:36:00 mast Exp $
 */
 
 #ifndef STRALLOC_H
@@ -330,6 +330,7 @@ PMOD_EXPORT struct pike_string *string_replace(struct pike_string *str,
 void init_shared_string_table(void);
 void cleanup_shared_string_table(void);
 void count_memory_in_strings(size_t *num, size_t *size);
+void visit_string (struct pike_string *s, int action);
 void gc_mark_string_as_referenced (struct pike_string *s);
 unsigned gc_touch_all_strings(void);
 void gc_mark_all_strings(void);
@@ -415,6 +416,10 @@ static INLINE void string_builder_binary_strcat(struct string_builder *s,
 
 #define ISCONSTSTR(X,Y) c_compare_string((X),Y,sizeof(Y)-sizeof(""))
 
+#define visit_string_ref(S, REF_TYPE)				\
+  visit_ref (pass_string (S), (REF_TYPE),			\
+	     (visit_thing_fn *) &visit_string, NULL)
+
 #ifdef DEBUG_MALLOC
 #define make_shared_string(X) \
  ((struct pike_string *)debug_malloc_pass(debug_make_shared_string(X)))