diff --git a/src/gc.c b/src/gc.c
index 5e79df10f98083d14fb0a184fee13ef76e68aa1c..1ae9fe970f949403200755dd50dd17ef6116231a 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.329 2008/10/11 18:44:11 grubba Exp $
+|| $Id: gc.c,v 1.330 2008/10/12 21:49:56 mast Exp $
 */
 
 #include "global.h"
@@ -4213,11 +4213,11 @@ PMOD_EXPORT TYPE_T type_from_visit_fn (visit_thing_fn *fn)
  * the things with the highest count.
  *
  * Every time we visit something we calculate its lookahead count as
- * either max (if it's found to be internal or a candidate), the same
- * as the source thing (if the followed ref is REF_TYPE_INTERNAL), or
- * the next lower count (otherwise). If the count is above zero and
- * the thing is either new or its old count was lower, it's added to
- * the work list.
+ * either max (if it's found to be referenced from an internal or
+ * candidate thing), the same as the source thing (if the followed ref
+ * is REF_TYPE_INTERNAL), or the next lower count (otherwise). If the
+ * count is above zero and the thing is either new or its old count
+ * was lower, it's added to the work list.
  *
  * mc_work_queue is a priority queue which always has the thing with
  * the highest lookahead count first, thereby ensuring breadth-first
@@ -4237,9 +4237,9 @@ PMOD_EXPORT TYPE_T type_from_visit_fn (visit_thing_fn *fn)
  * externally and should not be memory counted.
  *
  * If there's anything left in the complete list then it's internal
- * cyclic stuff. In that case we put those things into the work list
- * at max lookahead count, move the indirectly incomplete list back to
- * complete and repeat MC_PASS_LOOKAHEAD. Otherwise we're done.
+ * cyclic stuff. In that case we put those things into the work list,
+ * move the indirectly incomplete list back to complete and repeat
+ * MC_PASS_LOOKAHEAD. Otherwise we're done.
  */
 
 /* #define MEMORY_COUNT_DEBUG */
@@ -4250,35 +4250,48 @@ PMOD_EXPORT int mc_pass;
 PMOD_EXPORT size_t mc_counted_bytes;
 
 static int mc_lookahead, mc_block_pike_cycle_depth;
-static unsigned mc_count_revisits;
 static TYPE_FIELD mc_block_lookahead;
 static TYPE_FIELD mc_block_lookahead_default = BIT_PROGRAM|BIT_STRING|BIT_TYPE;
 /* Strings are blocked because they don't contain refs. Types are
  * blocked because they are acyclic and don't contain refs to anything
- * else. */
+ * but strings and other types. */
+
+static int mc_enqueued_noninternal;
+/* Set whenever something is enqueued in MC_PASS_LOOKAHEAD that isn't
+ * internal already. This is used to detect whether another
+ * MC_PASS_MARK_EXTERNAL is necessary. */
 
 static unsigned mc_ext_toggle_bias = 0;
 
 #define MC_PASS_LOOKAHEAD 1
 #define MC_PASS_MARK_EXTERNAL 2
 
-/* Set in when the refs emanating from a thing have been counted. */
-#define MC_FLAG_REFCOUNTED	0x01
-
 /* Set when a thing has become internal. */
-#define MC_FLAG_INTERNAL	0x02
+#define MC_FLAG_INTERNAL	0x01
+
+/* Set when an internal thing has been visited, i.e. after its refs
+ * has been gone through for the first time. This implies that the
+ * thing has been memory counted, and taken off mc_incomplete or
+ * mc_complete if it was there. */
+#define MC_FLAG_INT_VISITED	0x02
+
+/* Set when a non-internal thing has been visited. If
+ * MC_FLAG_INT_VISITED isn't then the thing is on one of mc_incomplete,
+ * mc_complete, or (in MC_PASS_MARK_EXTERNAL) mc_indirect. */
+#define MC_FLAG_LA_VISITED	0x04
 
 /* Set when a thing has become a candidate (i.e. complete and
  * referenced directly from an internal or candidate thing). This
  * flag is meaningless when MC_FLAG_INTERNAL is set. */
-#define MC_FLAG_CANDIDATE	0x04
+#define MC_FLAG_CANDIDATE	0x08
 
-/* Set when a thing is visited directly from a candidate thing. */
-#define MC_FLAG_CANDIDATE_REF	0x08
+/* Set when a thing is visited directly from an internal or candidate
+ * thing. */
+#define MC_FLAG_CANDIDATE_REF	0x10
 
-/* Set when a thing has been called with VISIT_COUNT_BYTES (after the
- * visit function has returned). */
-#define MC_FLAG_MEMCOUNTED	0x10
+/* The lookahead count should not change. Use when it has been lowered
+ * from pike_cycle_depth. */
+#define MC_FLAG_LA_COUNT_FIXED	0x20
 
 /* A toggle flag to mark external (i.e. incomplete and indirectly
  * incomplete) things in MC_PASS_MARK_EXTERNAL so that we don't
@@ -4287,7 +4300,7 @@ static unsigned mc_ext_toggle_bias = 0;
  * external if this is cleared. mc_ext_toggle_bias toggles every time
  * we leave MC_PASS_MARK_EXTERNAL, thus we avoid the work to go
  * through the externals clear the flag for the next round. */
-#define MC_FLAG_EXT_TOGGLE	0x20
+#define MC_FLAG_EXT_TOGGLE	0x40
 
 /* The value of IS_EXTERNAL is meaningless when MC_FLAG_INTERNAL is set. */
 #define IS_EXTERNAL(M)							\
@@ -4349,7 +4362,7 @@ static struct mc_marker *my_make_mc_marker (void *thing,
   m->queuepos = MAX_UINT32;
 #ifdef PIKE_DEBUG
   m->dl_prev = m->dl_next = (void *) (ptrdiff_t) -1;
-  m->la_count = 0;
+  m->la_count = ((unsigned INT16) -1) >> 1;
 #endif
   return m;
 }
@@ -4361,18 +4374,19 @@ static void describe_mc_marker (struct mc_marker *m)
 	   get_name_of_type (type_from_visit_fn (m->visit_fn)),
 	   m->thing, *(INT32 *) m->thing, m->int_refs, m->la_refs, m->la_count);
   if (m->queuepos != MAX_UINT32) fprintf (stderr, ", wq %u", m->queuepos);
-  if (m->flags & MC_FLAG_REFCOUNTED) fputs (", RC", stderr);
-  if (m->flags & MC_FLAG_INTERNAL) fputs (", INT", stderr);
+  if (m->flags & MC_FLAG_INTERNAL) fputs (", I", stderr);
+  if (m->flags & MC_FLAG_INT_VISITED) fputs (", IV", stderr);
+  if (m->flags & MC_FLAG_LA_VISITED) fputs (", LAV", stderr);
   if (m->flags & MC_FLAG_CANDIDATE) fputs (", C", stderr);
   if (m->flags & MC_FLAG_CANDIDATE_REF) fputs (", CR", stderr);
+  if (m->flags & MC_FLAG_LA_COUNT_FIXED) fputs (", CF", stderr);
   if (IS_EXTERNAL (m))
-    fputs (m->flags & MC_FLAG_INTERNAL ? ", (EXT)" : ", EXT", stderr);
-  if (m->flags & MC_FLAG_MEMCOUNTED) fputs (", MC", stderr);
+    fputs (m->flags & MC_FLAG_INTERNAL ? ", (E)" : ", E", stderr);
 }
 #endif
 
+/* Sentinel for the incomplete lookaheads list. */
 static struct mc_marker mc_incomplete = {
-  /* Sentinel for the incomplete lookaheads list. */
   (void *) (ptrdiff_t) -1,
   &mc_incomplete, &mc_incomplete,
   (void *) (ptrdiff_t) -1, (visit_thing_fn *) (ptrdiff_t) -1,
@@ -4380,8 +4394,12 @@ static struct mc_marker mc_incomplete = {
   -1, -1, MAX_UINT32, 0, (unsigned INT16) -1
 };
 
+/* Sentinel for the complete lookaheads list. The reason all complete
+ * things are tracked and not only the candidates is that elements
+ * then can be easily moved to mc_indirect (and back) without special
+ * cases when noncandidate complete things become indirectly
+ * incomplete. */
 static struct mc_marker mc_complete = {
-  /* Sentinel for the complete lookaheads list. */
   (void *) (ptrdiff_t) -1,
   &mc_complete, &mc_complete,
   (void *) (ptrdiff_t) -1, (visit_thing_fn *) (ptrdiff_t) -1,
@@ -4389,8 +4407,8 @@ static struct mc_marker mc_complete = {
   -1, -1, MAX_UINT32, 0, (unsigned INT16) -1
 };
 
+/* Sentinel for the indirectly incomplete lookaheads list. */
 static struct mc_marker mc_indirect = {
-  /* Sentinel for the indirectly incomplete lookaheads list. */
   (void *) (ptrdiff_t) -1,
   &mc_indirect, &mc_indirect,
   (void *) (ptrdiff_t) -1, (visit_thing_fn *) (ptrdiff_t) -1,
@@ -4424,13 +4442,12 @@ static struct mc_marker mc_indirect = {
   } while (0)
 
 #define DL_MOVE(FROM_LIST, TO_LIST) do {				\
-    assert (TO_LIST.dl_prev == &TO_LIST);				\
-    assert (TO_LIST.dl_next == &TO_LIST);				\
     if (FROM_LIST.dl_next != &FROM_LIST) {				\
+      struct mc_marker *to_list_last = TO_LIST.dl_prev;			\
       TO_LIST.dl_prev = FROM_LIST.dl_prev;				\
-      TO_LIST.dl_next = FROM_LIST.dl_next;				\
+      to_list_last->dl_next = FROM_LIST.dl_next;			\
       FROM_LIST.dl_prev->dl_next = &TO_LIST;				\
-      FROM_LIST.dl_next->dl_prev = &TO_LIST;				\
+      FROM_LIST.dl_next->dl_prev = to_list_last;			\
       FROM_LIST.dl_prev = FROM_LIST.dl_next = &FROM_LIST;		\
     }									\
   } while (0)
@@ -4540,6 +4557,9 @@ static void mc_wq_enqueue (struct mc_marker *m)
   int m_la_count = m->la_count;
 
   assert (mc_work_queue);
+#ifdef PIKE_DEBUG
+  assert (mc_lookahead < 0 || m_la_count != ((unsigned INT16) -1) >> 1);
+#endif
 
   if (m->queuepos != MAX_UINT32) {
     assert (m->queuepos < mc_wq_used);
@@ -4625,14 +4645,14 @@ static void pass_lookahead_visit_ref (void *thing, int ref_type,
 				      visit_thing_fn *visit_fn, void *extra)
 {
   struct mc_marker *ref_to = find_mc_marker (thing);
-  int ref_from_flags;
-  int old_la_count;		/* -1 flags new ref_to. */
-  int new_cand_ref, enqueue = 0;
-  TYPE_T type;
+  int ref_from_flags, ref_to_flags, old_la_count, ref_to_la_count;
+  int ref_added = 0, check_new_candidate = 0, la_count_handled = 0;
 
+  assert (mc_lookahead >= 0);
   assert (mc_pass == MC_PASS_LOOKAHEAD);
 #ifdef PIKE_DEBUG
   assert (mc_ref_from != (void *) (ptrdiff_t) -1);
+  assert (mc_ref_from->la_count != ((unsigned INT16) -1) >> 1);
 #endif
 
   ref_from_flags = mc_ref_from->flags;
@@ -4642,163 +4662,203 @@ static void pass_lookahead_visit_ref (void *thing, int ref_type,
   if (!ref_to) {
     ref_to = my_make_mc_marker (thing, visit_fn, extra);
     MC_DEBUG_MSG (ref_to, "visiting new thing");
-    assert (!(ref_from_flags & MC_FLAG_REFCOUNTED));
-    ref_to->la_count = 0;
-    old_la_count = -1;
+    assert (!(ref_from_flags & (MC_FLAG_INT_VISITED | MC_FLAG_LA_VISITED)));
+    ref_to_la_count = old_la_count = 0;
   }
   else if (ref_to->flags & MC_FLAG_INTERNAL) {
     /* Ignore refs to internal things. Can't treat them like other
      * things anyway since the int_refs aren't valid for the starting
      * points. */
     MC_DEBUG_MSG (ref_to, "ignored internal");
+    assert (ref_to->la_count != ((unsigned INT16) -1) >> 1);
     return;
   }
   else {
     MC_DEBUG_MSG (ref_to, "visiting old thing");
-    old_la_count = ref_to->la_count;
+    ref_to_la_count = old_la_count = ref_to->la_count;
+    assert (ref_to->la_count != ((unsigned INT16) -1) >> 1);
   }
 
-  /* Update int_refs and la_refs. */
+  ref_to_flags = ref_to->flags;
+
+#define SET_LA_COUNT_FOR_INT_OR_CF() do {				\
+    if (!(ref_to_flags & MC_FLAG_LA_COUNT_FIXED)) {			\
+      int cycle_depth;							\
+      if (visit_fn == (visit_thing_fn *) &visit_object &&		\
+	  !mc_block_pike_cycle_depth &&					\
+	  (cycle_depth =						\
+	   mc_cycle_depth_from_obj ((struct object *) thing)) >= 0) {	\
+	ref_to_la_count = cycle_depth;					\
+	ref_to_flags |= MC_FLAG_LA_COUNT_FIXED;				\
+	MC_DEBUG_MSG (ref_to, "la_count set to pike_cycle_depth");	\
+      }									\
+									\
+      else {								\
+	int count_from_source = ref_type & REF_TYPE_INTERNAL ?		\
+	  mc_ref_from->la_count : mc_ref_from->la_count - 1;		\
+									\
+	if (ref_to_la_count < mc_lookahead) {				\
+	  ref_to_la_count = mc_lookahead;				\
+	  MC_DEBUG_MSG (ref_to, "la_count raised to mc_lookahead");	\
+	}								\
+									\
+	if (ref_to_la_count < count_from_source) {			\
+	  ref_to_la_count = count_from_source;				\
+	  MC_DEBUG_MSG (ref_to, count_from_source == mc_ref_from->la_count ? \
+			"la_count raised to source count" :		\
+			"la_count raised to source count - 1");		\
+	}								\
+      }									\
+    }									\
+  } while (0)
 
-  if (ref_from_flags & MC_FLAG_INTERNAL) {
-    if (!(ref_from_flags & MC_FLAG_REFCOUNTED)) {
-      ref_to->int_refs++;
-      MC_DEBUG_MSG (ref_to, "added internal ref");
-    }
-    else {
+  if ((ref_from_flags & (MC_FLAG_INTERNAL | MC_FLAG_INT_VISITED)) ==
+      MC_FLAG_INTERNAL) {
+    if (ref_from_flags & MC_FLAG_LA_VISITED) {
       /* mc_ref_from is a former lookahead thing that has become internal. */
       assert (ref_to->la_refs > 0);
       ref_to->la_refs--;
       ref_to->int_refs++;
       MC_DEBUG_MSG (ref_to, "converted lookahead ref to internal");
     }
-  }
-  else
-    if (!(ref_from_flags & MC_FLAG_REFCOUNTED)) {
-      ref_to->la_refs++;
-      MC_DEBUG_MSG (ref_to, "added lookahead ref");
+    else {
+      ref_to->int_refs++;
+      MC_DEBUG_MSG (ref_to, "added internal ref");
+      ref_added = 1;
     }
-  assert (ref_to->int_refs + ref_to->la_refs <= *(INT32 *) thing);
+    assert (ref_to->int_refs + ref_to->la_refs <= *(INT32 *) thing);
 
-  /* Update candidate ref. */
+    /* Handle the target becoming internal. */
 
-  if (ref_from_flags & (MC_FLAG_INTERNAL | MC_FLAG_CANDIDATE)) {
-    ref_to->flags |= MC_FLAG_CANDIDATE_REF;
-    new_cand_ref = 1;
-  }
-  else
-    new_cand_ref = 0;
+    if (ref_to->int_refs == *(INT32 *) thing) {
+      assert (!(ref_to_flags & MC_FLAG_INTERNAL));
+      assert (!(ref_to_flags & MC_FLAG_INT_VISITED));
+      ref_to_flags |= MC_FLAG_INTERNAL;
 
-  /* Calculate new lookahead count from source thing. */
+      SET_LA_COUNT_FOR_INT_OR_CF();
 
-  if (ref_type & REF_TYPE_INTERNAL) {
-    if (old_la_count < mc_ref_from->la_count) {
-      ref_to->la_count = mc_ref_from->la_count;
-      MC_DEBUG_MSG (ref_to, "lookahead raised to source count");
-      enqueue = 1;
+      ref_to->flags = ref_to_flags;
+      ref_to->la_count = ref_to_la_count;
+      if (ref_to->queuepos != MAX_UINT32 && old_la_count == ref_to_la_count)
+	MC_DEBUG_MSG (ref_to, "already in queue");
+      else {
+	assert (ref_to->la_count >= old_la_count);
+	mc_wq_enqueue (ref_to);
+	MC_DEBUG_MSG (ref_to, "enqueued internal");
+      }
+      return;
     }
   }
-  else
-    if (old_la_count < mc_ref_from->la_count - 1) {
-      ref_to->la_count = mc_ref_from->la_count - 1;
-      MC_DEBUG_MSG (ref_to, "lookahead raised to source count - 1");
-      if (ref_to->la_count > 0) enqueue = 1;
-    }
 
-  /* Update internal/candidate/complete/incomplete status. */
+  if ((ref_from_flags & (MC_FLAG_INTERNAL | MC_FLAG_CANDIDATE)) &&
+      !(ref_to_flags & MC_FLAG_CANDIDATE_REF)) {
+    ref_to_flags |= MC_FLAG_CANDIDATE_REF;
+    MC_DEBUG_MSG (ref_to, "got candidate ref");
 
-  if (!(ref_from_flags & MC_FLAG_REFCOUNTED)) {
-    /* Only do this when traversing the link for the first time. */
+    SET_LA_COUNT_FOR_INT_OR_CF();
 
-    if (ref_to->int_refs == *(INT32 *) thing) {
-      ref_to->flags |= MC_FLAG_INTERNAL;
-      if (old_la_count >= 0) DL_REMOVE (ref_to);
-      if (ref_to->la_count < mc_lookahead) {
-	ref_to->la_count = mc_lookahead;
-	MC_DEBUG_MSG (ref_to, "made internal and raised count");
-	enqueue = 1;
-      }
-      else
-	MC_DEBUG_MSG (ref_to, "made internal");
-    }
+    check_new_candidate = la_count_handled = 1;
+  }
+
+  if (!(ref_from_flags & (MC_FLAG_INTERNAL | MC_FLAG_LA_VISITED))) {
+    assert (ref_to->int_refs + ref_to->la_refs < *(INT32 *) thing);
+    ref_to->la_refs++;
+    MC_DEBUG_MSG (ref_to, "added lookahead ref");
+    ref_added = 1;
+  }
+
+  if (ref_added && (ref_to->int_refs + ref_to->la_refs == *(INT32 *) thing)) {
+    MC_DEBUG_MSG (ref_to, "refs got complete");
+    check_new_candidate = 1;
 
-    else if (ref_to->int_refs + ref_to->la_refs == *(INT32 *) thing) {
-      if (old_la_count >= 0) DL_REMOVE (ref_to);
+    if (ref_to_flags & MC_FLAG_LA_VISITED) {
+      /* Move to mc_complete if it has been lookahead visited. In other
+       * cases this is handled after the lookahead visit is done. */
+      DL_REMOVE (ref_to);
       DL_ADD_LAST (mc_complete, ref_to);
-      if (ref_to->flags & MC_FLAG_CANDIDATE_REF) {
-	ref_to->flags |= MC_FLAG_CANDIDATE;
-	if (ref_to->la_count < mc_lookahead) {
-	  ref_to->la_count = mc_lookahead;
-	  MC_DEBUG_MSG (ref_to, "refs got complete for candidate ref'd, "
-			"raised count");
-	}
-	else
-	  MC_DEBUG_MSG (ref_to, "refs got complete for candidate ref'd");
-	enqueue = 1;
-      }
-      else
-	MC_DEBUG_MSG (ref_to, "refs got complete");
+      MC_DEBUG_MSG (ref_to, "moved to complete list");
     }
-
-    else
-      if (old_la_count < 0) {
-	DL_ADD_LAST (mc_incomplete, ref_to);
-	MC_DEBUG_MSG (ref_to, "added to mc_incomplete");
-      }
   }
 
-  else
-    /* Check ref_from_flags instead of (ref_to->flags & MC_FLAG_CANDIDATE_REF)
-     * since we're only interested if this link is the candidate ref. */
-    if (ref_from_flags & (MC_FLAG_INTERNAL | MC_FLAG_CANDIDATE) &&
-	!(ref_to->flags & MC_FLAG_CANDIDATE)) {
-      ref_to->flags |= MC_FLAG_CANDIDATE;
-      if (ref_to->la_count < mc_lookahead) {
-	ref_to->la_count = mc_lookahead;
-	MC_DEBUG_MSG (ref_to, "complete thing got candidate ref, "
-		      "raised count");
-      }
-      else
-	MC_DEBUG_MSG (ref_to, "complete thing got candidate ref");
-      enqueue = 1;
-    }
+  /* Handle the target becoming a candidate. */
 
-  /* Check mc_block_lookahead. */
-  /* Note that we still need markers on these since they might
-   * eventually become internal in which case the type block should
-   * be bypassed. */
+  if (check_new_candidate &&
+      (ref_to_flags & MC_FLAG_CANDIDATE_REF) &&
+      ref_to->int_refs + ref_to->la_refs == *(INT32 *) thing) {
+    assert (!(ref_to_flags & MC_FLAG_CANDIDATE));
+    assert (ref_to->la_refs > 0);
+    ref_to_flags |= MC_FLAG_CANDIDATE;
+    MC_DEBUG_MSG (ref_to, "made candidate");
 
-  type = type_from_visit_fn (visit_fn);
+    ref_to->flags = ref_to_flags;
+    ref_to->la_count = ref_to_la_count;
 
-  if (mc_block_lookahead & (1 << type) && !(ref_to->flags & MC_FLAG_INTERNAL))
-    MC_DEBUG_MSG (ref_to, "type is blocked - not enqueued");
+    if (mc_block_lookahead & (1 << type_from_visit_fn (visit_fn))) {
+      MC_DEBUG_MSG (ref_to, "type is blocked - not enqueued");
+      return;
+    }
 
-  else {
-    /* Check pike_cycle_depth. */
-
-    if (new_cand_ref) {
-      /* Got a new candidate reference. Check if we should set the
-       * count based on pike_cycle_depth. */
-      if (!mc_block_pike_cycle_depth && type == T_OBJECT) {
-	int cycle_depth = mc_cycle_depth_from_obj ((struct object *) thing);
-	if (cycle_depth >= 0) {
-	  if (cycle_depth > ref_to->la_count) enqueue = 1;
-	  ref_to->la_count = cycle_depth;
-	  MC_DEBUG_MSG (ref_to, "set count to pike_cycle_depth");
-	}
+    if (ref_to_la_count > 0) {
+      /* Always enqueue if the count allows it, even if it hasn't
+       * increased. That since MC_FLAG_CANDIDATE_REF must be propagated. */
+      if (ref_to->queuepos != MAX_UINT32 && old_la_count == ref_to_la_count)
+	MC_DEBUG_MSG (ref_to, "already in queue");
+      else {
+	assert (ref_to->la_count >= old_la_count);
+	mc_wq_enqueue (ref_to);
+	MC_DEBUG_MSG (ref_to, "enqueued candidate");
+	mc_enqueued_noninternal = 1;
       }
     }
+    else
+      MC_DEBUG_MSG (ref_to, "candidate not enqueued due to zero count");
+    return;
+  }
+
+  /* Normal handling. */
+
+  if (mc_block_lookahead & (1 << type_from_visit_fn (visit_fn))) {
+    ref_to->flags = ref_to_flags;
+    ref_to->la_count = ref_to_la_count;
+    MC_DEBUG_MSG (ref_to, "type is blocked - not enqueued");
+    return;
+  }
 
-    /* Enqueue if the lookahead count is raised. */
+  if (!la_count_handled && !(ref_to_flags & MC_FLAG_LA_COUNT_FIXED)) {
+    int cycle_depth;
+    int count_from_source = ref_type & REF_TYPE_INTERNAL ?
+      mc_ref_from->la_count : mc_ref_from->la_count - 1;
 
-    if (enqueue) {
-      mc_wq_enqueue (ref_to);
-      if (old_la_count >= 0) mc_count_revisits++;
-      MC_DEBUG_MSG (ref_to, "enqueued");
+    if (ref_to_la_count < count_from_source) {
+      ref_to_la_count = count_from_source;
+      MC_DEBUG_MSG (ref_to, count_from_source == mc_ref_from->la_count ?
+		    "la_count raised to source count" :
+		    "la_count raised to source count - 1");
     }
-    else
-      MC_DEBUG_MSG (ref_to, "not enqueued");
+
+    if (visit_fn == (visit_thing_fn *) &visit_object &&
+	!mc_block_pike_cycle_depth &&
+	(cycle_depth =
+	 mc_cycle_depth_from_obj ((struct object *) thing)) >= 0 &&
+	cycle_depth < ref_to_la_count) {
+      /* pike_cycle_depth is only allowed to lower the lookahead count
+       * for things that aren't internal, candidates, or candidate ref'd. */
+      ref_to_la_count = cycle_depth;
+      ref_to_flags |= MC_FLAG_LA_COUNT_FIXED;
+      MC_DEBUG_MSG (ref_to, "la_count lowered to pike_cycle_depth");
+    }
+  }
+
+  ref_to->flags = ref_to_flags;
+  ref_to->la_count = ref_to_la_count;
+  assert (ref_to->la_count >= old_la_count);
+  if (ref_to->la_count > old_la_count) {
+    mc_wq_enqueue (ref_to);
+    MC_DEBUG_MSG (ref_to, "enqueued");
+    mc_enqueued_noninternal = 1;
   }
+  else
+    MC_DEBUG_MSG (ref_to, "not enqueued");
 }
 
 static void pass_mark_external_visit_ref (void *thing, int ref_type,
@@ -4809,21 +4869,71 @@ static void pass_mark_external_visit_ref (void *thing, int ref_type,
   assert (mc_pass == MC_PASS_MARK_EXTERNAL);
 
   if (ref_to) {
-    if (!(ref_to->flags & MC_FLAG_INTERNAL)) {
-      /* Only interested in existing lookahead things. */
+    if ((ref_to->flags & (MC_FLAG_INT_VISITED | MC_FLAG_LA_VISITED)) ==
+	MC_FLAG_LA_VISITED) {
+      /* Only interested in existing lookahead things, except those on
+       * the "fringe" that haven't been visited. */
 
-      if (!IS_EXTERNAL (ref_to)) {
-	DL_REMOVE (ref_to);
+      if (IS_EXTERNAL (ref_to))
+	MC_DEBUG_MSG (ref_to, "already external");
+      else {
 	FLAG_EXTERNAL (ref_to);
+	DL_REMOVE (ref_to);
 	DL_ADD_LAST (mc_indirect, ref_to);
-	MC_DEBUG_MSG (ref_to, "marked external");
+	MC_DEBUG_MSG (ref_to, "marked external - moved to indirect list");
 	assert (ref_to->int_refs + ref_to->la_refs == *(INT32 *) thing);
       }
-      else
-	MC_DEBUG_MSG (ref_to, "already external");
     }
     else
-      MC_DEBUG_MSG (ref_to, "ignored internal");
+      MC_DEBUG_MSG (ref_to, ref_to->flags & MC_FLAG_INTERNAL ?
+		    "ignored internal" : "ignored fringe thing");
+  }
+}
+
+static void current_only_visit_ref (void *thing, int ref_type,
+				    visit_thing_fn *visit_fn, void *extra)
+/* This is used when count_memory has a negative lookahead. It only
+ * recurses through REF_TYPE_INTERNAL references. Note that most
+ * fields in mc_marker aren't used. */
+{
+  struct mc_marker *ref_to = find_mc_marker (thing);
+  int ref_from_flags;
+
+  assert (mc_lookahead < 0);
+#ifdef PIKE_DEBUG
+  assert (mc_ref_from != (void *) (ptrdiff_t) -1);
+#endif
+
+  ref_from_flags = mc_ref_from->flags;
+  assert (ref_from_flags & MC_FLAG_INTERNAL);
+  assert (!(ref_from_flags & MC_FLAG_INT_VISITED));
+
+  if (!ref_to) {
+    ref_to = my_make_mc_marker (thing, visit_fn, extra);
+    MC_DEBUG_MSG (ref_to, "got new thing");
+  }
+  else if (ref_to->flags & MC_FLAG_INTERNAL) {
+    /* Ignore refs to the starting points. Can't treat them like other
+     * things anyway since the int_refs aren't valid. */
+    MC_DEBUG_MSG (ref_to, "ignored starting point");
+    return;
+  }
+  else
+    MC_DEBUG_MSG (ref_to, "got old thing");
+
+  if (!(ref_type & REF_TYPE_INTERNAL)) {
+    MC_DEBUG_MSG (ref_to, "ignored non-internal ref");
+    return;
+  }
+
+  ref_to->int_refs++;
+  MC_DEBUG_MSG (ref_to, "added really internal ref");
+  assert (ref_to->int_refs <= *(INT32 *) thing);
+
+  if (ref_to->int_refs == *(INT32 *) thing) {
+    ref_to->flags |= MC_FLAG_INTERNAL;
+    mc_wq_enqueue (ref_to);
+    MC_DEBUG_MSG (ref_to, "enqueued internal");
   }
 }
 
@@ -4834,13 +4944,13 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
 #ifdef PIKE_DEBUG
     if (!m) Pike_fatal ("mc_marker not found for %p.\n", thing);
 #endif
-    if ((m->flags & (MC_FLAG_INTERNAL|MC_FLAG_MEMCOUNTED)) == MC_FLAG_INTERNAL)
+    if ((m->flags & (MC_FLAG_INTERNAL|MC_FLAG_INT_VISITED)) == MC_FLAG_INTERNAL)
       return 1;
   }
   return 0;
 }
 
-/*! @decl int count_memory (int(0..)|mapping(string:int) options, @
+/*! @decl int count_memory (int|mapping(string:int) options, @
  *!         array|multiset|mapping|object|program|string|type|int... things)
  *! @appears Pike.count_memory
  *!
@@ -4888,8 +4998,8 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
  *! discover those cycles. When @[Pike.count_memory] visits such
  *! objects, it uses that as the lookahead when going through the
  *! references emanating from them. Thus, assuming objects adhere to
- *! this convention, you should rarely have to specify a higher
- *! lookahead than 1 to this function.
+ *! this convention, you should rarely have to specify a lookahead
+ *! higher than zero to this function.
  *!
  *! Note that @expr{pike_cycle_depth@} can also be set to zero to
  *! effectively stop the lookahead from continuing through the object.
@@ -4898,12 +5008,13 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
  *!
  *! @param options
  *!   If this is an integer, it specifies the maximum lookahead
- *!   distance. 0 counts only the memory of the given @[things],
- *!   without following any references, 1 extends the count to all
+ *!   distance. -1 counts only the memory of the given @[things],
+ *!   without following any references, 0 extends the count to all
  *!   their referenced things as long as there are no cycles (except
- *!   if @expr{pike_cycle_depth@} is found in objects - see above), 2
- *!   makes it cover cycles of length 2 (i.e. where two things point
- *!   at each other), and so on.
+ *!   if @expr{pike_cycle_depth@} is found in objects - see above), 1
+ *!   makes it cover cycles of length 1 (e.g. a thing points to
+ *!   itself), 2 handles cycles of length 2 (e.g. where two things
+ *!   point at each other), and so on.
  *!
  *!   However, the lookahead is by default blocked by programs, i.e.
  *!   it never follows references emanating from programs. That since
@@ -4915,9 +5026,9 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
  *!   mapping instead:
  *!
  *!   @mapping
- *!     @member int(0..) lookahead
+ *!     @member int lookahead
  *!       The maximum lookahead distance, as described above. Defaults
- *!       to 1 if missing.
+ *!       to 0 if missing.
  *!     @member int block_arrays
  *!     @member int block_mappings
  *!     @member int block_multisets
@@ -4929,7 +5040,7 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
  *!       zero value. Only programs are blocked by default.
  *!     @member int block_pike_cycle_depth
  *!       Do not heed @expr{pike_cycle_depth@} values found in
- *!       objects. This happens by default if the lookahead is 0.
+ *!       objects. This is implicit if the lookahead is negative.
  *!     @member int return_count
  *!       Return the number of things that memory was counted for,
  *!       instead of the byte count. (This is the same number
@@ -4941,6 +5052,13 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
  *!       If set then the value is replaced with an array containing
  *!       the things that were visited but turned out to have external
  *!       references (within the limited lookahead).
+ *!     @member int collect_direct_externals
+ *!       If set then the value is replaced with an array containing
+ *!       the things found during the lookahead that (appears to) have
+ *!       direct external references. This list is a subset of the
+ *!       @expr{collect_externals@} list. It is useful if you get
+ *!       unexpected global references to your data structure which
+ *!       you want to track down.
  *!     @member int collect_stats
  *!       If this is nonzero then the mapping is extended with more
  *!       elements containing statistics from the search; see below.
@@ -4960,15 +5078,21 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
  *!       Number of things that were visited through the lookahead but
  *!       were found to be external.
  *!     @member int visits
- *!       Number of times things were visited in total.
+ *!       Number of times things were visited in total. This figure
+ *!       includes visits to various internal things that aren't
+ *!       visible from the pike level, so it might be larger than what
+ *!       is apparently motivated by the numbers above.
  *!     @member int revisits
  *!       Number of times the same things were revisited. This can
  *!       occur in the lookahead when a thing is encountered through a
- *!       shorter path than the one it first got visited through.
+ *!       shorter path than the one it first got visited through. It
+ *!       also occurs in resolved cycles. Like @expr{visits@}, this
+ *!       count can include things that aren't visible from pike.
  *!     @member int rounds
- *!       Number of search rounds. Whenever the lookahead discovers a
- *!       cycle, another round has to be made to search for more
- *!       internal things referenced from the cycle.
+ *!       Number of search rounds. This is usually 1 or 2. More rounds
+ *!       are necessary only when blocked types turn out to be
+ *!       (acyclic) internal, so that they need to be counted and
+ *!       recursed anyway.
  *!     @member int work_queue_alloc
  *!       The number of elements that was allocated to store the work
  *!       queue which is used to keep track of the things to visit
@@ -4976,7 +5100,8 @@ PMOD_EXPORT int mc_count_bytes (void *thing)
  *!       maximum number of things the queue actually held.
  *!     @member int size
  *!       The memory occupied by the internal things. This is the same
- *!       as the return value, but it's put here too for convenience.
+ *!       as the normal return value, but it's put here too for
+ *!       convenience.
  *!   @endmapping
  *!
  *! @param things
@@ -5016,7 +5141,7 @@ void f_count_memory (INT32 args)
 {
   struct svalue *collect_internal = NULL;
   unsigned count_internal, count_cyclic, count_visited;
-  unsigned count_visits, count_rounds;
+  unsigned count_visits, count_revisits, count_rounds;
   int collect_stats = 0, return_count = 0;
 
   if (args < 1)
@@ -5032,14 +5157,16 @@ void f_count_memory (INT32 args)
 
     MAKE_CONST_STRING (ind, "lookahead");
     if ((val = low_mapping_string_lookup (opts, ind))) {
-      if (val->type != T_INT || val->u.integer < 0)
+      if (val->type != T_INT)
 	SIMPLE_ARG_ERROR ("count_memory", 1,
-			  "\"lookahead\" must be a non-negative integer.");
-      mc_lookahead = val->u.integer > (unsigned INT16) -1 ?
-	(unsigned INT16) -1 : val->u.integer;
+			  "\"lookahead\" must be an integer.");
+      mc_lookahead =
+	val->u.integer > (unsigned INT16) -1 ? (unsigned INT16) -1 :
+	val->u.integer < 0 ? -1 :
+	val->u.integer;
     }
     else
-      mc_lookahead = 1;
+      mc_lookahead = 0;
 
 #define CHECK_BLOCK_FLAG(NAME, TYPE_BIT) do {				\
       MAKE_CONST_STRING (ind, NAME);					\
@@ -5074,15 +5201,16 @@ void f_count_memory (INT32 args)
   }
 
   else {
-    if (Pike_sp[-args].type != T_INT || Pike_sp[-args].u.integer < 0)
-      SIMPLE_ARG_TYPE_ERROR ("count_memory", 1, "int(0..)|mapping(string:int)");
-    mc_lookahead = Pike_sp[-args].u.integer > (unsigned INT16) -1 ?
-      (unsigned INT16) -1 : Pike_sp[-args].u.integer;
+    if (Pike_sp[-args].type != T_INT)
+      SIMPLE_ARG_TYPE_ERROR ("count_memory", 1, "int|mapping(string:int)");
+    mc_lookahead =
+      Pike_sp[-args].u.integer > (unsigned INT16) -1 ? (unsigned INT16) -1 :
+      Pike_sp[-args].u.integer < 0 ? -1 :
+      Pike_sp[-args].u.integer;
   }
 
   init_mc_marker_hash();
 
-  if (!mc_lookahead) mc_block_pike_cycle_depth = 1;
   if (pike_cycle_depth_str.type == PIKE_T_FREE) {
     pike_cycle_depth_str.type = T_STRING;
     MAKE_CONST_STRING (pike_cycle_depth_str.u.string, "pike_cycle_depth");
@@ -5146,7 +5274,7 @@ void f_count_memory (INT32 args)
 	else {
 	  struct mc_marker *m =
 	    my_make_mc_marker (s->u.ptr, visit_fn_from_type[s->type], NULL);
-	  m->flags = MC_FLAG_INTERNAL;
+	  m->flags |= MC_FLAG_INTERNAL;
 	  if (!mc_block_pike_cycle_depth && s->type == T_OBJECT) {
 	    int cycle_depth = mc_cycle_depth_from_obj (s->u.object);
 	    if (throw_value.type != PIKE_T_FREE) {
@@ -5156,7 +5284,7 @@ void f_count_memory (INT32 args)
 	      throw_severity = THROW_ERROR;
 	      pike_throw();
 	    }
-	    m->la_count = cycle_depth == -1 ? mc_lookahead : cycle_depth;
+	    m->la_count = cycle_depth >= 0 ? cycle_depth : mc_lookahead;
 	  }
 	  else
 	    m->la_count = mc_lookahead;
@@ -5183,25 +5311,46 @@ void f_count_memory (INT32 args)
 
   mc_counted_bytes = 0;
   count_internal = count_cyclic = count_visited = 0;
-  count_visits = mc_count_revisits = count_rounds = 0;
+  count_visits = count_revisits = count_rounds = 0;
+
+  visit_ref = mc_lookahead < 0 ?
+    current_only_visit_ref : pass_lookahead_visit_ref;
 
   do {
     count_rounds++;
+    mc_enqueued_noninternal = 0;
 
 #ifdef MEMORY_COUNT_DEBUG
-    fputs ("MC_PASS_LOOKAHEAD\n", stderr);
+    fprintf (stderr, "[%d] MC_PASS_LOOKAHEAD\n", count_rounds);
 #endif
     mc_pass = MC_PASS_LOOKAHEAD;
-    visit_ref = pass_lookahead_visit_ref;
 
     while ((mc_ref_from = mc_wq_dequeue())) {
       int action;
 
-      if ((mc_ref_from->flags & (MC_FLAG_INTERNAL|MC_FLAG_MEMCOUNTED)) ==
-	  MC_FLAG_INTERNAL) {
+      assert (!(mc_ref_from->flags & MC_FLAG_INT_VISITED));
+
+      if (mc_ref_from->flags & MC_FLAG_INTERNAL) {
 	action = VISIT_COUNT_BYTES; /* Memory count this. */
 	MC_DEBUG_MSG (NULL, "enter with byte counting");
 
+	mc_ref_from->visit_fn (mc_ref_from->thing, action, mc_ref_from->extra);
+	count_visits++;
+
+	if (mc_ref_from->flags & MC_FLAG_LA_VISITED) {
+	  count_revisits++;
+	  DL_REMOVE (mc_ref_from);
+	  MC_DEBUG_MSG (NULL, "leave - removed from list");
+	}
+	else {
+	  if (collect_stats &&
+	      type_from_visit_fn (mc_ref_from->visit_fn) <= MAX_TYPE)
+	    count_visited++;
+	  MC_DEBUG_MSG (NULL, "leave");
+	}
+
+	mc_ref_from->flags |= MC_FLAG_INT_VISITED;
+
 	if (return_count || collect_stats || collect_internal) {
 	  TYPE_T type = type_from_visit_fn (mc_ref_from->visit_fn);
 	  if (type <= MAX_TYPE) {
@@ -5220,12 +5369,38 @@ void f_count_memory (INT32 args)
       }
 
       else {
+	assert (mc_lookahead >= 0);
 	action = VISIT_NORMAL;
 	MC_DEBUG_MSG (NULL, "enter");
-      }
 
-      count_visits++;
-      mc_ref_from->visit_fn (mc_ref_from->thing, action, mc_ref_from->extra);
+	mc_ref_from->visit_fn (mc_ref_from->thing, action, mc_ref_from->extra);
+	count_visits++;
+
+	if (mc_ref_from->flags & MC_FLAG_LA_VISITED) {
+	  count_revisits++;
+	  MC_DEBUG_MSG (NULL, "leave (revisit)");
+	}
+
+	else {
+	  if (collect_stats &&
+	      type_from_visit_fn (mc_ref_from->visit_fn) <= MAX_TYPE)
+	    count_visited++;
+
+	  mc_ref_from->flags |= MC_FLAG_LA_VISITED;
+
+	  /* The reason for fixing the lists here is to avoid putting
+	   * the "fringe" things that we never visit onto them. */
+	  if (mc_ref_from->int_refs + mc_ref_from->la_refs <
+	      *(INT32 *) mc_ref_from->thing) {
+	    DL_ADD_LAST (mc_incomplete, mc_ref_from);
+	    MC_DEBUG_MSG (NULL, "leave - added to incomplete list");
+	  }
+	  else {
+	    DL_ADD_LAST (mc_complete, mc_ref_from);
+	    MC_DEBUG_MSG (NULL, "leave - added to complete list");
+	  }
+	}
+      }
 
       if (throw_value.type != PIKE_T_FREE) {
 	exit_mc_marker_hash();
@@ -5234,24 +5409,29 @@ void f_count_memory (INT32 args)
 	throw_severity = THROW_ERROR;
 	pike_throw();
       }
-
-      if (!(mc_ref_from->flags & MC_FLAG_REFCOUNTED)) {
-	mc_ref_from->flags |= MC_FLAG_REFCOUNTED;
-	if (collect_stats &&
-	    type_from_visit_fn (mc_ref_from->visit_fn) <= MAX_TYPE)
-	  count_visited++;
-      }
-
-      if (mc_ref_from->flags & MC_FLAG_INTERNAL)
-	mc_ref_from->flags |= MC_FLAG_MEMCOUNTED;
-      MC_DEBUG_MSG (NULL, "leave");
     }
 #if defined (PIKE_DEBUG) || defined (MEMORY_COUNT_DEBUG)
     mc_ref_from = (void *) (ptrdiff_t) -1;
 #endif
 
+    /* If no things that might be indirectly incomplete have been
+     * enqueued then there's no need to do another mark external pass. */
+    if (!mc_enqueued_noninternal) {
+      DL_MAKE_EMPTY (mc_complete);
+      break;
+    }
+
+    if (mc_lookahead < 0) {
+      assert (mc_incomplete.dl_prev == &mc_incomplete);
+      assert (mc_incomplete.dl_next == &mc_incomplete);
+      assert (mc_complete.dl_prev == &mc_complete);
+      assert (mc_complete.dl_next == &mc_complete);
+      break;
+    }
+
 #ifdef MEMORY_COUNT_DEBUG
-    fputs ("MC_PASS_MARK_EXTERNAL\n", stderr);
+    fprintf (stderr, "[%d] MC_PASS_MARK_EXTERNAL, "
+	     "traversing the incomplete list\n", count_rounds);
 #endif
     mc_pass = MC_PASS_MARK_EXTERNAL;
     visit_ref = pass_mark_external_visit_ref;
@@ -5272,29 +5452,54 @@ void f_count_memory (INT32 args)
 	 * indirect externals appear. */
 	for (m = list->dl_next; m != list; m = m->dl_next) {
 	  TYPE_T type = type_from_visit_fn (m->visit_fn);
+	  assert (!(m->flags & MC_FLAG_INTERNAL));
+	  assert (m->flags & MC_FLAG_LA_VISITED);
+	  assert (list != &mc_incomplete || !(m->flags & MC_FLAG_CANDIDATE));
 	  if (mc_block_lookahead & (1 << type))
-	    MC_DEBUG_MSG (m, "type blocked - not visiting");
+	    MC_DEBUG_MSG (m, "type is blocked - not visiting");
 	  else {
-	    MC_DEBUG_MSG (m, "visiting external");
+#ifdef MEMORY_COUNT_DEBUG
+	    mc_ref_from = m;
+	    MC_DEBUG_MSG (NULL, "visiting external");
+#endif
 	    count_visits++;
+	    count_revisits++;
 	    m->visit_fn (m->thing, VISIT_NORMAL, m->extra);
 	  }
 	}
-	if (list == &mc_incomplete) list = &mc_indirect;
+	if (list == &mc_incomplete) {
+	  list = &mc_indirect;
+#ifdef MEMORY_COUNT_DEBUG
+	  fprintf (stderr, "[%d] MC_PASS_MARK_EXTERNAL, "
+		   "traversing the indirect list\n", count_rounds);
+#endif
+	}
 	else break;
       }
+
+#if defined (PIKE_DEBUG) || defined (MEMORY_COUNT_DEBUG)
+      mc_ref_from = (void *) (ptrdiff_t) -1;
+#endif
     }
 
     if (DL_IS_EMPTY (mc_complete)) break;
 
+#ifdef MEMORY_COUNT_DEBUG
+    fprintf (stderr, "[%d] MC_PASS_MARK_EXTERNAL, "
+	     "enqueuing cyclic internals\n", count_rounds);
+#endif
+
     {
       /* We've found some internal cyclic stuff. Put it in the work
        * list for the next round. */
       struct mc_marker *m = mc_complete.dl_next;
       assert (m != &mc_complete);
       do {
-	DL_REMOVE (m);
+	assert (!(m->flags & (MC_FLAG_INTERNAL | MC_FLAG_INT_VISITED)));
 	m->flags |= MC_FLAG_INTERNAL;
+	assert (m->flags & (MC_FLAG_CANDIDATE | MC_FLAG_LA_VISITED));
+	assert (!(mc_block_lookahead &
+		  (1 << type_from_visit_fn (m->visit_fn))));
 	/* The following assertion implies that the lookahead count
 	 * already has been raised as it should. */
 	assert (m->flags & MC_FLAG_CANDIDATE_REF);
@@ -5302,7 +5507,7 @@ void f_count_memory (INT32 args)
 	if (collect_stats && type_from_visit_fn (m->visit_fn) <= MAX_TYPE)
 	  count_cyclic++;
 	MC_DEBUG_MSG (m, "enqueued cyclic internal");
-	m = mc_complete.dl_next;
+	m = m->dl_next;
       } while (m != &mc_complete);
     }
 
@@ -5323,6 +5528,9 @@ void f_count_memory (INT32 args)
       }
     }
 #endif
+
+    /* Prepare for next MC_PASS_LOOKAHEAD round. */
+    visit_ref = pass_lookahead_visit_ref;
   } while (1);
 
 #ifdef MEMORY_COUNT_DEBUG
@@ -5334,7 +5542,7 @@ void f_count_memory (INT32 args)
 	   "count_memory stats: %u visits, %u revisits, %u rounds\n",
 	   count_internal, count_cyclic,
 	   count_visited - count_internal,
-	   count_visits, mc_count_revisits, count_rounds);
+	   count_visits, count_revisits, count_rounds);
 #ifdef PIKE_DEBUG
   {
     size_t num, size;
@@ -5371,7 +5579,7 @@ void f_count_memory (INT32 args)
       INSERT_STAT ("cyclic", count_cyclic);
       INSERT_STAT ("external", count_visited - count_internal);
       INSERT_STAT ("visits", count_visits);
-      INSERT_STAT ("revisits", mc_count_revisits);
+      INSERT_STAT ("revisits", count_revisits);
       INSERT_STAT ("rounds", count_rounds);
       INSERT_STAT ("work_queue_alloc", mc_wq_size);
       INSERT_STAT ("size", mc_counted_bytes);
@@ -5384,19 +5592,20 @@ void f_count_memory (INT32 args)
 	struct mc_marker *m, *list = &mc_incomplete;
 	while (1) {
 	  /* Collect things from the mc_incomplete and mc_indirect lists. */
-	  for (m = list->dl_next; m != list; m = m->dl_next)
-	    if (m->flags & MC_FLAG_REFCOUNTED) {
-	      TYPE_T type = type_from_visit_fn (m->visit_fn);
-	      if (type <= MAX_TYPE) {
-		Pike_sp->type = type;
-		Pike_sp->subtype = 0;
-		Pike_sp->u.ptr = m->thing;
-		add_ref ((struct ref_dummy *) m->thing);
-		dmalloc_touch_svalue (Pike_sp);
-		Pike_sp++;
-		DO_AGGREGATE_ARRAY (120);
-	      }
+	  for (m = list->dl_next; m != list; m = m->dl_next) {
+	    TYPE_T type = type_from_visit_fn (m->visit_fn);
+	    assert (!(m->flags & MC_FLAG_INTERNAL));
+	    assert (m->flags & MC_FLAG_LA_VISITED);
+	    if (type <= MAX_TYPE) {
+	      Pike_sp->type = type;
+	      Pike_sp->subtype = 0;
+	      Pike_sp->u.ptr = m->thing;
+	      add_ref ((struct ref_dummy *) m->thing);
+	      dmalloc_touch_svalue (Pike_sp);
+	      Pike_sp++;
+	      DO_AGGREGATE_ARRAY (120);
 	    }
+	  }
 	  if (list == &mc_incomplete) list = &mc_indirect;
 	  else break;
 	}
@@ -5404,6 +5613,31 @@ void f_count_memory (INT32 args)
       args++;
       mapping_string_insert (opts, ind, Pike_sp - 1);
     }
+
+    MAKE_CONST_STRING (ind, "collect_direct_externals");
+    if ((val = low_mapping_string_lookup (opts, ind)) &&
+	!UNSAFE_IS_ZERO (val)) {
+      BEGIN_AGGREGATE_ARRAY (count_visited - count_internal) {
+	/* Collect things from the mc_incomplete list. */
+	struct mc_marker *m;
+	for (m = mc_incomplete.dl_next; m != &mc_incomplete; m = m->dl_next) {
+	  TYPE_T type = type_from_visit_fn (m->visit_fn);
+	  assert (!(m->flags & MC_FLAG_INTERNAL));
+	  assert (m->flags & MC_FLAG_LA_VISITED);
+	  if (type <= MAX_TYPE) {
+	    Pike_sp->type = type;
+	    Pike_sp->subtype = 0;
+	    Pike_sp->u.ptr = m->thing;
+	    add_ref ((struct ref_dummy *) m->thing);
+	    dmalloc_touch_svalue (Pike_sp);
+	    Pike_sp++;
+	    DO_AGGREGATE_ARRAY (120);
+	  }
+	}
+      } END_AGGREGATE_ARRAY;
+      args++;
+      mapping_string_insert (opts, ind, Pike_sp - 1);
+    }
   }
 
   mc_pass = 0;