Bugs: Browse | Submit New | Admin

[#15425] Leak with regexp in method with no local vars.

Date:
2007-11-07 13:14
Priority:
3
Submitted By:
(zimbatm)
Assigned To:
Shyouhei Urabe (shyouhei)
Category:
Language / Runtime / Core Libraries
State:
Open
Platform:
 
Summary:
Leak with regexp in method with no local vars.

Detailed description
This leak happens in 1.8.6-p66 and 111. See attached file for minimal test case. (needs linux)

Hint : gc.c:1256 ?

Add A Comment: Notepad

Please login


Followup

Message
Date: 2008-06-08 02:29
Sender: Roger Pack

looks like it has [at least the svn trunk version of 1.8.7]
I could be mistaken but I believe it was fixed just before the
'release' of 1.8.7
more certainly in trunk, though
Date: 2008-06-06 07:02
Sender: Matthias Tarasiewicz

is this still a problem in ruby 1.8.7 or has it been fixed there
already?
Date: 2008-05-28 09:17
Sender: Roger Pack

appears to be patched on the 1.8.6 branch rev 16534.  works on
mac os, too.

Date: 2008-05-21 06:55
Sender: Matthias Tarasiewicz

the leak still seems to exist in p114 and p115, as well as in
trunk. see the "god" mailinglist for further comments
on the memory leak
http://groups.google.com/group/god-rb/browse_thread/thread/01cca2
b7c4a581c2

maybe also related to
http://rubyforge.org/tracker/?group_id=426&atid=1698&func
=detail&aid=19088
Date: 2008-04-30 16:12
Sender: Roger Pack

unable to add a file for some reason.
Here's the patch.
Index: parse.y
=================================================================
==
--- parse.y     (revision 16244)
+++ parse.y     (working copy)
@@ -5821,7 +5821,8 @@
                if (!(ruby_scope->flags & SCOPE_CLONE))
                    xfree(ruby_scope->local_tbl);
            }
-           ruby_scope->local_tbl = local_tbl();
+            ruby_scope->local_vars[-1] = 0;
+            ruby_scope->local_tbl = local_tbl();
        }
     }
     local_pop();

Date: 2008-04-29 15:20
Sender: Roger Pack

AFAIK this bug is still in ruby.
-R
Date: 2008-04-15 20:08
Sender: Roger Pack

I'd imagine it's stil in the code.
To overcome it:
Adding the line

add ruby_scope->local_vars[-1] = 0;

to 
$Author: matz $
-  $Date: 2006/07/12 11:10:21 $
+  $Date: 2006/08/07 03:36:52 $
  created at: Fri May 28 18:02:42 JST 1993

  Copyright (C) 1993-2004 Yukihiro Matsumoto
@@ -8066,7 +8066,7 @@ top_local_setup_gen(struct parser_params
            rb_mem_clear(vars+i, len-i);
        }
        else {
-            *vars++ = 0;
+            *vars++ = (VALUE)ruby_scope;
            rb_mem_clear(vars, len);
        }
        ruby_scope->local_vars = vars;
@@ -8081,7 +8081,6 @@ top_local_setup_gen(struct parser_params
        if (ruby_scope->local_tbl &&
ruby_scope->local_vars[-1] == 0) {
        xfree(ruby_scope->local_tbl);
        }
-        ruby_scope->local_vars[-1] = 0;
        ruby_scope->local_tbl = local_tbl();
    }
    }
Date: 2008-04-07 21:24
Sender: Jodi Showers

can anyone comment on the state of this bug?

is it avail in a particular patch level?

thank you. Jodi
Date: 2007-11-09 07:35
Sender: Roger Pack

Thank you my friend.
Adding the line

ruby_scope->local_vars[-1] = 0;

to it seems to fix the problem with now solid low memory use.
I don't know what the other part of that patch does, either.

I did notice in the hunting that I think the stack push/pop could
use a little work to be more efficient, there are some (real)
error messages thrown by valgrind, and that it might be more
efficient to maintain and a freed list (unordered).  Things to
think about :)
GL.
-Roger


 /* so...a freelist and allocated list, per heap, would prevent
you from having
 to traverse (at all) sections of it that are free--never have
to go there if it
's free, ever ever.
>
> a freelist per heap (or a global freelist?) that is only
'reset' every so ofte
n (thus allowing for heap reclamation) would allow in the sweep
phase for possib
ly skipping certain pages that are all free
>
> basically no matter what, all allocated memory will
be swept.
> A global allocated list/free list would allow only allocated
to be traversed.
and heaps could be reclaimed by 'resetting' everything every
so often, I suppose
.
> So would a freelist per heap be better somehow?  You'd know
if the heap is tot
ally free or used up without worry.
>
>
> So if you just had a per heap free list (+count)
>
> keep a global pointer to the next heap with free stuff in
it.  Then it's still
 O(1) to get it.
> i
> but on force recycle how does it go to its heap?
>
> with a global allocated and global free it's hard without
doing a mass sweep t
o reclaim. Who cares about reclaiming?
>

Don't remark free stuffs (need 1/10 global freelist or not).
Constant heap sizes.
a fixed this.
smarter collection.
Date: 2007-11-09 06:50
Sender: Marc de la Gueronniere

It seems the leak was introduced by this change from ruby-cvs:17330
.

$Author: matz $
-  $Date: 2006/07/12 11:10:21 $
+  $Date: 2006/08/07 03:36:52 $
  created at: Fri May 28 18:02:42 JST 1993

  Copyright (C) 1993-2004 Yukihiro Matsumoto
@@ -8066,7 +8066,7 @@ top_local_setup_gen(struct parser_params
            rb_mem_clear(vars+i, len-i);
        }
        else {
-            *vars++ = 0;
+            *vars++ = (VALUE)ruby_scope;
            rb_mem_clear(vars, len);
        }
        ruby_scope->local_vars = vars;
@@ -8081,7 +8081,6 @@ top_local_setup_gen(struct parser_params
        if (ruby_scope->local_tbl &&
ruby_scope->local_vars[-1] == 0) {
        xfree(ruby_scope->local_tbl);
        }
-        ruby_scope->local_vars[-1] = 0;
        ruby_scope->local_tbl = local_tbl();
    }
    }

---------------------------------
It seems that local_vars[-1] == 0 is used to check local_tbl
is owned by the scope (instead of the node) in top_local_setup
and in obj_free. Reverting ruby-cvs:17330 fixes the memory leak.

I lack the proper expertise of Ruby's internal to recommend a
patch.
Date: 2007-11-09 04:20
Sender: Roger Pack

It appears that when you have
def a
 ''.whatever
end
it parses it as a node without a var table (makes sense--no
vars).
Then when you get to eval.c:5922 there's no body no table, so
ruby_scope->local_tbl is set to nil.

Then when regex tries to save something, it says 'what there's
no var space? I'll create my own' (kind of -- calls
special_local_set('~', val);
which as far as I can tell never frees its memory since its a
heap outside of the heap.
Date: 2007-11-08 21:12
Sender: Roger Pack

==13734== 1,600 bytes in 100 blocks are definitely lost in loss
record 3 of 6
==13734==    at 0x40215D8: malloc (in
/usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==13734==    by 0x4067992: ruby_xmalloc (in
/usr/lib/libruby18.so.1.8.5)
==13734==    by 0x40878DF: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x4087CFC: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x40A20F1: rb_reg_search
(in /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x40BA190: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x4049102: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x4050741: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x40514B7: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x4058662: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x4051050: (within /usr/lib/libruby18.so.1.8.5)
==13734==    by 0x40514B7: (within /usr/lib/libruby18.so.1.8.5)


But it only leaks when it's in just the right configuration--I'd
guess this is the leak so it's only for regex (maybe).
Date: 2007-11-08 17:20
Sender: Roger Pack

Note this occurs with 1.8.5 as well, and that you may need to
un-set RUBYOPT (i.e. not RUBYOPT=-rubygems) for it to work.
Date: 2007-11-08 16:38
Sender: Roger Pack

And smaller

 def g
   ''.gsub('','')
 end
 
loop do
  g
  GC.start
end
Date: 2007-11-08 16:07
Sender: Roger Pack

class String
   def basename
       return ''.gsub('','') # regex doesn't matter. is it gsub's
fault? faulty parameter marshalling?
   end
end

loop do
  ''.basename
end

seems to do it, too (optionally put GC.start in there to see
that it is actually growing slowly, but growing indeed)
Date: 2007-11-08 15:07
Sender: Roger Pack

This reproduces it, too (slightly less code)

class String
   def basename # seems to need this inner call aspect (?)
       self.gsub(//,'')
   end
end

pid = Process.pid
gen = 0

loop do
  "ab".basename#gsub(/^.*::/, '') 
  
  GC.start
  if gen % 100 == 0
    gen = 0
    #p File.read("/proc/#{pid}/status").grep(/VmPeak|Vm
Rss/i)
  end
  gen += 1
end

(slow leak, number of objects allocted to ObjectSpace stays
same)

however, if you do any of the following, it fixes it

change this to
   def basename
       a = self.gsub(//,'')
       return a # works now
   end

or 
   def basename
       reg = //
       self.gsub(reg,'') # works now
   end

So it may be a parameter passing problem?

Attached Files:

Name Description Download
random2.rb Leak test. Download

Changes:

Field Old Value Date By
File Added2760: random2.rb2007-11-07 13:14zimbatm