diff --git a/src/builtin_functions.c b/src/builtin_functions.c
index 7e6e05170a2092f5f80b991ca228bcbbd41640ef..65807ff54766f2966dca97c813626ce2c272de50 100644
--- a/src/builtin_functions.c
+++ b/src/builtin_functions.c
@@ -7609,15 +7609,31 @@ PMOD_EXPORT void f__memory_usage(INT32 args)
   f_aggregate_mapping(DO_NOT_WARN(Pike_sp - ss));
 }
 
-/* estimate the size of an svalue, not including objects.
-   this is used from size_object
+/* Estimate the size of an svalue, not including objects.
+   this is used from size_object.
+
+   It should not include the size of the svalue itself, so the basic
+   types count as 0 bytes.
+
+   This is an estimate mainly because it is very hard to know to whom
+   a certain array/mapping/multiset or string "belongs".
+
+   The returned size will be the memory usage of the svalue divided by
+   the number of references to it.
 */
-static unsigned int rec_size_svalue( struct svalue *s, struct mapping **m )
+
+struct string_header
+{
+    PIKE_STRING_CONTENTS;
+};
+
+unsigned int rec_size_svalue( struct svalue *s, struct mapping **m )
 {
     unsigned int res = 0;
     int i;
     ptrdiff_t node_ref;
     INT32 e;
+    struct svalue *x;
     struct keypair *k;
 
     switch( s->type )
@@ -7625,9 +7641,9 @@ static unsigned int rec_size_svalue( struct svalue *s, struct mapping **m )
         case PIKE_T_STRING:
             /* FIXME: This makes assumptions about the threshold for short strings. */
             if( s->u.string->flags & STRING_IS_SHORT )
-                return 16 / s->u.string->refs;
+                return (16+sizeof(struct string_header)) / s->u.string->refs;
             return ((s->u.string->len << s->u.string->size_shift) +
-                    sizeof(struct pike_string)) / s->u.string->refs;
+                    sizeof(struct string_header)) / s->u.string->refs;
         case PIKE_T_INT:
         case PIKE_T_OBJECT:
         case PIKE_T_FLOAT:
@@ -7635,11 +7651,15 @@ static unsigned int rec_size_svalue( struct svalue *s, struct mapping **m )
         case PIKE_T_TYPE:
             return 0;
     }
+    if( !m ) return 0;
 
     if( !*m )
         *m = allocate_mapping( 10 );
-    else if( low_mapping_lookup( *m, s ) )
-        return 0; // already counted.
+    else if( (x = low_mapping_lookup( *m, s )) )
+    {
+        /* Already counted. Use the old size. */
+        return x->u.integer;
+    }
 
     low_mapping_insert( *m, s, &svalue_int_one, 0 );
     switch( s->type )
@@ -7651,27 +7671,42 @@ static unsigned int rec_size_svalue( struct svalue *s, struct mapping **m )
             break;
 
         case PIKE_T_MULTISET:
-            res = sizeof(struct multiset);
+            res = sizeof(struct multiset) + sizeof(struct multiset_data);
             node_ref = multiset_last( s->u.multiset );
             while( node_ref != -1 )
             {
                 res += rec_size_svalue( get_multiset_value (s->u.multiset, node_ref), m )
-                    + sizeof(struct svalue);
+                    /* each node has the index and left/right node pointers. */
+                    + sizeof(struct svalue) + (sizeof(void*)*2);
                 node_ref = multiset_prev( s->u.multiset, node_ref );
             }
             break;
 
         case PIKE_T_MAPPING:
-            res = sizeof(struct mapping) + sizeof(struct mapping_data);
-            NEW_MAPPING_LOOP( s->u.mapping->data  )
+            res = sizeof(struct mapping);
             {
-                res += rec_size_svalue( &k->ind, m );
-                res += rec_size_svalue( &k->val, m );
-                res += sizeof( struct keypair );
+                struct mapping_data *d = s->u.mapping->data;
+                struct keypair *f = d->free_list;
+                int data_size = sizeof( struct mapping_data );
+                data_size += d->hashsize * sizeof(struct keypair *) - sizeof(struct keypair *);
+                while( f )
+                {
+                    data_size += sizeof(struct keypair);
+                    f = f->next;
+                }
+                NEW_MAPPING_LOOP( s->u.mapping->data  )
+                {
+                    data_size += rec_size_svalue( &k->ind, m );
+                    data_size += rec_size_svalue( &k->val, m );
+                    data_size += sizeof( struct keypair );
+                }
+                res += data_size / (d->hardlinks+1);
             }
             break;
     }
-    return res / *s->u.refs;
+    res /= *s->u.refs;
+    low_mapping_lookup(*m,s)->u.integer = res;
+    return res;
 }
 
 /*! @decl int size_object(object o)
diff --git a/src/builtin_functions.h b/src/builtin_functions.h
index c8658d253fd5c3f32c8d4be24eb29dbceab9754f..f1f6c47384419b61d6558cc1e2d0a7354db1b4f9 100644
--- a/src/builtin_functions.h
+++ b/src/builtin_functions.h
@@ -164,6 +164,7 @@ PMOD_EXPORT void f_inherit_list(INT32 args);
 PMOD_EXPORT void f_function_defined(INT32 args);
 void init_builtin_efuns(void);
 void exit_builtin_efuns(void);
+unsigned int rec_size_svalue( struct svalue *s, struct mapping **m );
 
 /* From iterators.cmod. */
 PMOD_EXPORT void f_get_iterator(INT32 args);