So you want to make Ruby leak memory? Here's a simple way

Make a hash:

h = {‘a’ => 1, ‘b’ => 2, ‘c’ => 3}

And make another out of it:

Hash[h]

Do that a lot. Watch RAM vanish. GC cleans up the objects out of
object
space just fine, but RAM trickles away nonetheless.

Here is a test program:

loop do
attrs = Hash[‘this’,‘that’,‘the other’,‘enough’,‘stuff’,‘for six’]
Hash[attrs]
end

I have tested it on an installation of RedHat Enterprise Linux with
1.8.1,
1.8.2, 1.8.4, and 1.8.5 and also under 1.8.4 (one click installer) on
WinXP Home.

I’ve ran it under valgrind and just straight, and have inserted code to
run GC after each loop iteration. The behavior is consistent. If you
eliminate the Hash[attrs] line, there is no leak.

Kirk H.

Interesting. Does this patch fixes it?

Index: hash.c

RCS file: /src/ruby/hash.c,v
retrieving revision 1.128.2.16
diff -u -r1.128.2.16 hash.c
— hash.c 6 Jul 2006 15:44:26 -0000 1.128.2.16
+++ hash.c 29 Aug 2006 00:14:17 -0000
@@ -328,6 +328,7 @@
hash = hash_alloc(klass);

    RHASH(hash)->ifnone = Qnil;
  •   st_free_table(RHASH(hash)->tbl);
      RHASH(hash)->tbl = st_copy(RHASH(argv[0])->tbl);
    
      return hash;

On Tue, 29 Aug 2006, Kent S. wrote:

  hash = hash_alloc(klass);

  RHASH(hash)->ifnone = Qnil;
  •   st_free_table(RHASH(hash)->tbl);
    RHASH(hash)->tbl = st_copy(RHASH(argv[0])->tbl);
    
    return hash;
    

It does indeed. Yay.

Kirk H.

On 8/28/06, Kent S. [email protected] wrote:

    hash = hash_alloc(klass);

    RHASH(hash)->ifnone = Qnil;
  •   st_free_table(RHASH(hash)->tbl);
      RHASH(hash)->tbl = st_copy(RHASH(argv[0])->tbl);
    
      return hash;
    

Bravo, Kent. Props.

On 8/28/06, [email protected] [email protected] wrote:

I’ve ran it under valgrind and just straight, and have inserted code to
run GC after each loop iteration. The behavior is consistent. If you
eliminate the Hash[attrs] line, there is no leak.

Kirk H.

Wow…that seems to be a pretty major leak to have made it this long
w/o being noticed. Maybe that type of behavior isn’t repeated often
enough in most programs to expose the leak?

  • rob

On Tue, 29 Aug 2006, Rob S. wrote:

Wow…that seems to be a pretty major leak to have made it this long
w/o being noticed. Maybe that type of behavior isn’t repeated often
enough in most programs to expose the leak?

It’s a small enough leak that it takes a lot of iterations to start to
add
up. And while the threshold of pain involved in chasing that thing down
wasn’t horrible, it’s high enough that I can understand people doing
what
I have done for a long time:

Oh, that process is getting a bit big.
kill restart

Or they assume that it’s a bug in their code somewhere and do the same
thing.

Look at the discussion about Mutex leaking last week.

BTW, my test code makes use of a number of mutexes and heavy use of
threading, and after running 120k hits through it, the RAM usage is rock
solid at 13.8Mb. Normally after that many hits it would have grown to
around 45mb. So, this is very pleasing to me.

Kirk H.

[email protected] wrote in message
news:[email protected]

+++ hash.c 29 Aug 2006 00:14:17 -0000
@@ -328,6 +328,7 @@
hash = hash_alloc(klass);

  RHASH(hash)->ifnone = Qnil;
  •   st_free_table(RHASH(hash)->tbl);
    RHASH(hash)->tbl = st_copy(RHASH(argv[0])->tbl);
    
    return hash;
    

It does indeed. Yay.

Nice!

Sure thing. The table initialization in this case is redundant.

Hi,

At Tue, 29 Aug 2006 09:15:27 +0900,
Kent S. wrote in [ruby-talk:211236]:

Interesting. Does this patch fixes it?

Yes, and modified it slightly.

Index: hash.c

RCS file: /cvs/ruby/src/ruby/hash.c,v
retrieving revision 1.128.2.16
diff -p -U 2 -r1.128.2.16 hash.c
— hash.c 6 Jul 2006 15:44:26 -0000 1.128.2.16
+++ hash.c 29 Aug 2006 03:43:58 -0000
@@ -224,7 +224,8 @@ rb_hash_foreach(hash, func, farg)
}

+static VALUE hash_alloc0 _((VALUE));
static VALUE hash_alloc _((VALUE));
static VALUE
-hash_alloc(klass)
+hash_alloc0(klass)
VALUE klass;
{
@@ -233,9 +234,19 @@ hash_alloc(klass)

 hash->ifnone = Qnil;
  • hash->tbl = st_init_table(&objhash);

    return (VALUE)hash;
    }

+static VALUE
+hash_alloc(klass)

  • VALUE klass;
    +{
  • VALUE hash = hash_alloc0(klass);
  • RHASH(hash)->tbl = st_init_table(&objhash);
  • return hash;
    +}

VALUE
rb_hash_new()
@@ -326,7 +337,5 @@ rb_hash_s_create(argc, argv, klass)

 if (argc == 1 && TYPE(argv[0]) == T_HASH) {
  • hash = hash_alloc(klass);
  • RHASH(hash)->ifnone = Qnil;
  • hash = hash_alloc0(klass);
    RHASH(hash)->tbl = st_copy(RHASH(argv[0])->tbl);

On Tue, 29 Aug 2006 10:26:17 +0900, khaines wrote:

It’s a small enough leak that it takes a lot of iterations to start to add
up.

… but no memory leak is small enough to evade you forever, Kirk :slight_smile:

Grrrrreat work!
s.

PS. Did you get my mail last week?

Hi,

In message “Re: So you want to make Ruby leak memory? Here’s a simple
way…”
on Tue, 29 Aug 2006 12:46:51 +0900, [email protected] writes:

|At Tue, 29 Aug 2006 09:15:27 +0900,
|Kent S. wrote in [ruby-talk:211236]:
|> Interesting. Does this patch fixes it?
|
|Yes, and modified it slightly.

OK, can you commit?

						matz.

On 8/29/06, Stefan S. [email protected] wrote:

On Tue, 29 Aug 2006 10:26:17 +0900, khaines wrote:

It’s a small enough leak that it takes a lot of iterations to start to
add
up.

… but no memory leak is small enough to evade you forever, Kirk :slight_smile:

it will also appear only shortly after a new release though

Grrrrreat work!

s.

Indeed

PS. Did you get my mail last week?


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein