diff --git a/src/array.c b/src/array.c
index e75094065934000c5790fbd880d97c8f6deef877..8cea9ac70c9a42dfcdf0364724133e9a58e0738f 100644
--- a/src/array.c
+++ b/src/array.c
@@ -2797,6 +2797,7 @@ void check_all_arrays(void)
 
 PMOD_EXPORT void visit_array (struct array *a, int action, void *extra)
 {
+  visit_enter(a, T_ARRAY, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -2818,6 +2819,7 @@ PMOD_EXPORT void visit_array (struct array *a, int action, void *extra)
     for (e = 0; e < s; e++)
       visit_svalue (ITEM (a) + e, ref_type, extra);
   }
+  visit_leave(a, T_ARRAY, extra);
 }
 
 static void gc_check_array(struct array *a)
diff --git a/src/constants.c b/src/constants.c
index 6071d0c9996af8314814f499bfa69605029c1ca8..7cf807dff13fce36666fc708d1d6ab0ac2efcab2 100644
--- a/src/constants.c
+++ b/src/constants.c
@@ -196,6 +196,7 @@ PMOD_EXPORT void quick_add_efun(const char *name, ptrdiff_t name_length,
 
 PMOD_EXPORT void visit_callable (struct callable *c, int action, void *extra)
 {
+  visit_enter(c, T_STRUCT_CALLABLE, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -216,6 +217,7 @@ PMOD_EXPORT void visit_callable (struct callable *c, int action, void *extra)
 
   /* Looks like the c->prog isn't refcounted..? */
   /* visit_program_ref (c->prog, REF_TYPE_NORMAL); */
+  visit_leave(c, T_STRUCT_CALLABLE, extra);
 }
 
 #ifdef PIKE_DEBUG
diff --git a/src/gc.c b/src/gc.c
index 6918c27de4aeb68de521332ce89a3da6e54cd4af..a03b55592752fd2530bcc71c7892834fc065e562 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -4399,6 +4399,8 @@ void cleanup_gc(void)
 /* Visit things API */
 
 PMOD_EXPORT visit_ref_cb *visit_ref = NULL;
+PMOD_EXPORT visit_enter_cb *visit_enter = NULL;
+PMOD_EXPORT visit_leave_cb *visit_leave = NULL;
 
 /* Be careful if extending this with internal types like
  * T_MAPPING_DATA and T_MULTISET_DATA; there's code that assumes
@@ -5283,6 +5285,14 @@ static void current_only_visit_ref (void *thing, int ref_type,
   }
 }
 
+static void ignore_visit_enter(void *thing, int type, void *extra)
+{
+}
+
+static void ignore_visit_leave(void *thing, int type, void *extra)
+{
+}
+
 PMOD_EXPORT int mc_count_bytes (void *thing)
 {
   if (mc_pass == MC_PASS_LOOKAHEAD) {
@@ -5608,7 +5618,9 @@ void f_count_memory (INT32 args)
   mc_wq_used = 1;
 
   assert (!mc_pass);
+  assert (visit_enter == NULL);
   assert (visit_ref == NULL);
+  assert (visit_leave == NULL);
 
   free_svalue (&throw_value);
   mark_free_svalue (&throw_value);
@@ -5693,8 +5705,10 @@ void f_count_memory (INT32 args)
   count_internal = count_cyclic = count_visited = 0;
   count_visits = count_revisits = count_rounds = 0;
 
+  visit_enter = ignore_visit_enter;
   visit_ref = mc_lookahead < 0 ?
     current_only_visit_ref : pass_lookahead_visit_ref;
+  visit_leave = ignore_visit_leave;
 
   do {
     count_rounds++;
@@ -6014,7 +6028,9 @@ void f_count_memory (INT32 args)
   }
 
   mc_pass = 0;
+  visit_enter = NULL;
   visit_ref = NULL;
+  visit_leave = NULL;
 
   DL_MAKE_EMPTY (mc_incomplete);
   DL_MAKE_EMPTY (mc_indirect);
diff --git a/src/gc.h b/src/gc.h
index 1697503f0461f09fb441c4d210cae4ed618ba7aa..f9786cfd3acc816972ddc4bf3f05ff5561569d2f 100644
--- a/src/gc.h
+++ b/src/gc.h
@@ -627,12 +627,17 @@ extern int gc_in_cycle_check;
  * use thread local storage when the time comes. */
 
 /* A visit_thing_fn is made for every type of refcounted block. An
- * INT32 refcounter is assumed to be first in every block. The
+ * INT32 refcounter is assumed to be first in every block.
+ * The visit_thing_fn should start by calling the visit_enter function
+ * with src_thing, the base type and extra as arguments. Then
  * visit_thing_fn should call visit_ref exactly once for every
  * refcounted ref inside src_thing, in a stable order. dst_thing is
  * then the target of the ref, ref_type describes the type of the ref
  * itself (REF_TYPE_*), visit_dst is the visit_thing_fn for the target
  * block, and extra is an arbitrary value passed along to visit_dst.
+ * After visit_ref has been called for all refcounted things, visit_leave
+ * should be called with the same arguments as the initial call of
+ * visit_enter.
  *
  * action identifies some action that the visit_thing_fn should take
  * (VISIT_*). Also, visit_ref_cb is likely to get a return value that
@@ -642,9 +647,13 @@ extern int gc_in_cycle_check;
  * immediately, queued and called later, or not called at all. */
 
 typedef void visit_thing_fn (void *src_thing, int action, void *extra);
+typedef void visit_enter_cb (void *thing, int type, void *extra);
 typedef void visit_ref_cb (void *dst_thing, int ref_type,
 			   visit_thing_fn *visit_dst, void *extra);
+typedef void visit_leave_cb (void *thing, int type, void *extra);
+PMOD_EXPORT extern visit_enter_cb *visit_enter;
 PMOD_EXPORT extern visit_ref_cb *visit_ref;
+PMOD_EXPORT extern visit_leave_cb *visit_leave;
 
 #define REF_TYPE_STRENGTH 0x03	/* Bits for normal/weak/strong. */
 #define REF_TYPE_NORMAL	0x00	/* Normal (nonweak and nonstrong) ref. */
diff --git a/src/mapping.c b/src/mapping.c
index d8a4ec48e161e6b855ac69ae665d05959d01ddeb..6ff88dd47fededc725e145d123c8a0ea1851819b 100644
--- a/src/mapping.c
+++ b/src/mapping.c
@@ -2472,6 +2472,7 @@ void check_all_mappings(void)
 static void visit_mapping_data (struct mapping_data *md, int action,
 				void *extra)
 {
+  visit_enter(md, T_MAPPING_DATA, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -2498,10 +2499,12 @@ static void visit_mapping_data (struct mapping_data *md, int action,
       visit_svalue (&k->val, val_ref_type, extra);
     }
   }
+  visit_leave(md, T_MAPPING_DATA, extra);
 }
 
 PMOD_EXPORT void visit_mapping (struct mapping *m, int action, void *extra)
 {
+  visit_enter(m, T_MAPPING, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -2517,6 +2520,7 @@ PMOD_EXPORT void visit_mapping (struct mapping *m, int action, void *extra)
 
   visit_ref (m->data, REF_TYPE_INTERNAL,
 	     (visit_thing_fn *) &visit_mapping_data, extra);
+  visit_leave(m, T_MAPPING, extra);
 }
 
 #ifdef MAPPING_SIZE_DEBUG
diff --git a/src/multiset.c b/src/multiset.c
index abfdd86b3013edc1830cc66408231b97bd09c5ca..f1fd4d25a722da4d28a963edba5edf33bf45738a 100644
--- a/src/multiset.c
+++ b/src/multiset.c
@@ -3485,6 +3485,7 @@ PMOD_EXPORT ptrdiff_t multiset_get_nth (struct multiset *l, size_t n)
 static void visit_multiset_data (struct multiset_data *msd, int action,
 				 void *extra)
 {
+  visit_enter(msd, T_MULTISET_DATA, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -3522,10 +3523,12 @@ static void visit_multiset_data (struct multiset_data *msd, int action,
 	visit_svalue (&ind, ind_ref_type, extra);
       } while ((node = low_multiset_next (node)));
   }
+  visit_leave(msd, T_MULTISET_DATA, extra);
 }
 
 PMOD_EXPORT void visit_multiset (struct multiset *l, int action, void *extra)
 {
+  visit_enter(l, T_MULTISET, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -3541,6 +3544,7 @@ PMOD_EXPORT void visit_multiset (struct multiset *l, int action, void *extra)
 
   visit_ref (l->msd, REF_TYPE_INTERNAL,
 	     (visit_thing_fn *) &visit_multiset_data, extra);
+  visit_leave(l, T_MULTISET, extra);
 }
 
 unsigned gc_touch_all_multisets (void)
diff --git a/src/object.c b/src/object.c
index 7ed36edabf3dcd4f8086f68a47b5b4dd9b115e09..9af58ab75fdbda31eccf6e68c08daa953e5154fb 100644
--- a/src/object.c
+++ b/src/object.c
@@ -2089,6 +2089,7 @@ PMOD_EXPORT void visit_object (struct object *o, int action, void *extra)
 
   if (o->next == o) return; /* Fake object used by compiler */
 
+  visit_enter(o, T_OBJECT, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -2210,6 +2211,7 @@ PMOD_EXPORT void visit_object (struct object *o, int action, void *extra)
       if (PARENT_INFO (o)->parent)
 	visit_object_ref (PARENT_INFO (o)->parent, REF_TYPE_STRONG, extra);
   }
+  visit_leave(o, T_OBJECT, extra);
 }
 
 PMOD_EXPORT void visit_function (const struct svalue *s, int ref_type,
diff --git a/src/pike_types.c b/src/pike_types.c
index b1d9f90ab02be34a3d0713f95aac94bde7aa7856..db79db7d3e928178d7ae70431f00960eaf6fa5ce 100644
--- a/src/pike_types.c
+++ b/src/pike_types.c
@@ -8913,6 +8913,7 @@ PMOD_EXPORT void *find_type(struct pike_type *t,
 
 PMOD_EXPORT void visit_type (struct pike_type *t, int action, void *extra)
 {
+  visit_enter(t, PIKE_T_TYPE, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -8921,6 +8922,7 @@ PMOD_EXPORT void visit_type (struct pike_type *t, int action, void *extra)
       break;
 #endif
     case VISIT_COMPLEX_ONLY:
+      visit_leave(t, PIKE_T_TYPE, extra);
       return;
     case VISIT_COUNT_BYTES:
       mc_counted_bytes += sizeof (struct pike_type);
@@ -8979,6 +8981,7 @@ PMOD_EXPORT void visit_type (struct pike_type *t, int action, void *extra)
       break;
 #endif /* PIKE_DEBUG */
   }
+  visit_leave(t, PIKE_T_TYPE, extra);
 }
 
 #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)
diff --git a/src/program.c b/src/program.c
index d932b94dacf89dcdc209a2ab6ce51698851716f7..95e65e3be6125ee46b36cc2e64892f1bec04ce73 100644
--- a/src/program.c
+++ b/src/program.c
@@ -11029,6 +11029,7 @@ void cleanup_program(void)
 
 PMOD_EXPORT void visit_program (struct program *p, int action, void *extra)
 {
+  visit_enter(p, T_PROGRAM, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -11081,6 +11082,7 @@ PMOD_EXPORT void visit_program (struct program *p, int action, void *extra)
     if (p->parent)
       visit_program_ref (p->parent, REF_TYPE_STRONG, extra);
   }
+  visit_leave(p, T_PROGRAM, extra);
 }
 
 static void gc_check_program(struct program *p);
diff --git a/src/stralloc.c b/src/stralloc.c
index f6f28de660d205f596a2190db5dbceb68d262c94..067c56844f79061606799e8690841a3e83ab169c 100644
--- a/src/stralloc.c
+++ b/src/stralloc.c
@@ -2273,6 +2273,7 @@ void count_memory_in_strings(size_t *num, size_t *size)
 
 PMOD_EXPORT void visit_string (struct pike_string *s, int action, void *extra)
 {
+  visit_enter(s, T_STRING, extra);
   switch (action) {
 #ifdef PIKE_DEBUG
     default:
@@ -2285,6 +2286,7 @@ PMOD_EXPORT void visit_string (struct pike_string *s, int action, void *extra)
       mc_counted_bytes += memory_in_string (s);
       break;
   }
+  visit_leave(s, T_STRING, extra);
 }
 
 #ifdef PIKE_DEBUG