# Memory Question

**URL:** <https://rubytalk.org/t/memory-question/25234>\
**Category:** ruby-talk\
**Created:** [26 February 2006 09:51 UTC](https://rubytalk.org/t/memory-question/25234 "2006-02-26T09:51:07Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Zach\_Dennis1](https://avatars.discourse-cdn.com/v4/letter/z/ea5d25/32.png) [@Zach\_Dennis1](https://rubytalk.org/u/Zach_Dennis1)\
**Post date:** [26 February 2006 09:51 UTC](https://rubytalk.org/t/memory-question/25234/1 "2006-02-26T09:51:07Z")

</div>

I am doing some large queries with Mysql and the memory that gets allocated never seems to go back to the system. In this test I am querying 47,000 records.

My setup for my test is ubuntu breezy, rails 1.0, mysql 5.0.18 and a compiled mysql 2.7 ruby driver running in production mode. I have wrote a helper to my program which monitors object count, memory utilization and a threshold of 10% to determine how many objects are sticking around and how many are being garbage collected.

On first run...

&nbsp;&nbsp;&nbsp;Loaded suite test/unit/table1\_test  
&nbsp;&nbsp;&nbsp;Started  
&nbsp;&nbsp;&nbsp;String count 73685  
&nbsp;&nbsp;&nbsp;Building query 21Mb  
&nbsp;&nbsp;&nbsp;String count 73745

&nbsp;&nbsp;&nbsp;Received query results 89Mb  
&nbsp;&nbsp;&nbsp;String count 901034  
&nbsp;&nbsp;&nbsp;Threshold breaker String (827236) started w/ 73743 ended w/ 900979  
&nbsp;&nbsp;&nbsp;Threshold breaker Table1 47000 started w/ 0 ended w/ 47000

&nbsp;&nbsp;&nbsp;Starting GC 89Mb  
&nbsp;&nbsp;&nbsp;String count 25041  
&nbsp;&nbsp;&nbsp;Done with GC 82Mb  
&nbsp;&nbsp;&nbsp;Threshold breaker String -48704 started w/ 73743 ended w/ 25039

The first block of text shows that when I started my test ruby was utilizing 21Mb of memory and over 70,000 strings were in existance. After the query results were constructed there was over 900,000 strings in existance and my ruby process had grown to 89Mb. The threshold is shown as a gain in 47,000 Table1 objects and 827,236 Strings since the the first object count was captured (which occured right before the original String count).

After garbage collecting the Stringcount is down 48,704 from when it was first captured, and there is no threshold break for Table1 because when the program first started there were 0 in existance. Thus meaning that 0 Table1 objects are in existance. However the memory size never seems to leave 82Mb. It never seems to leave that size.

If I query again, memory goes up to 133Mb, after all Strings and ActiveRecord models have been garbage collected, memory goes down ever so slightly again. And this continues as long as I keep querying.

The test is broken out into three methods:  
&nbsp;&nbsp;- test\_build\_mem\_usage (count objects, perform query, store results in local variable )  
&nbsp;&nbsp;- test\_gc (count objects, GC.start)  
&nbsp;&nbsp;- test\_z (done, recount objects since GC is done)

I guess my biggest unknown at the moment is...as I do large queries is ruby just hanging onto that space? Why would it keep growing for the next time I did 47,000 item query, if it already had unused space available from my last query?

At the bottom of this post is the actual test schema and test code I was using.

Zach

---- start schema ----  
create table table1 (  
&nbsp;&nbsp;&nbsp;id int unsigned not null auto\_increment,  
&nbsp;&nbsp;&nbsp;description varchar(255),  
&nbsp;&nbsp;&nbsp;store\_name varchar(255),  
&nbsp;&nbsp;&nbsp;address1 varchar(40),  
&nbsp;&nbsp;&nbsp;address2 varchar(40),  
&nbsp;&nbsp;&nbsp;city varchar(40),  
&nbsp;&nbsp;&nbsp;state varchar(15),  
&nbsp;&nbsp;&nbsp;zip\_code varchar(5),  
&nbsp;&nbsp;&nbsp;primary key( id )  
)TYPE=MyISAM;

---- start test code ----  
# hook into the Rails environment  
require File.dirname(\_\_FILE\_\_) + '/../test\_helper'

require 'table1'

class Object  
&nbsp;&nbsp;&nbsp;def count\_objects  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;objects = Hash.new{ |h,k| h[k]=0 }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectSpace.each\_object{ |obj| objects[obj.class] += 1 }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;objects  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def print\_threshold\_breakers hsh1, hsh2, threshold  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# threshold is in percentages  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;threshold = 1.0 + 1.0 / threshold

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hsh2.each\_key do |key|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max\_num = hsh1[key] \* threshold  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min\_num = hsh1[key] / threshold

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if hsh2[key] \> max\_num or hsh2[key] \< min\_num  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putsf "Threshold breaker #{key.to\_s}", "(#{hsh2[key]-hsh1[key]}) started w/ #{hsh1[key]} ended w/ #{hsh2[key]}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def count\_objects\_for clazz  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c = 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectSpace.each\_object{ |o| c+=1 if o.is\_a? clazz }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# get the top two lines from top  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line\_arr = `ps -p #{Process.pid} -F`.split( /\n/ )

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# split the line array into columns of headers and data  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arr1, arr2 = line\_arr.map{ |line| line.split( /\s+/ ) }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# force the same number of elements in arr2 as there are in arr1 by joining any leftover elements  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;column\_arr = [arr1]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;column\_arr \<\< arr2[0 .. arr1.size-2] + arr2[arr1.size-1 .. arr2.size-1].join( ' ' ).to\_a

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# get column/data key pair array  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keypair\_arr = column\_arr.transpose

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# create hash  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hsh = {}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keypair\_arr.each{ |e| hsh[e[0]] = e[1] }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# grab results from RSS, which are stored in Kb  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(hsh['RSS'].to\_i / 1024.0).round.to\_s \<\< "Mb"  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def putsf label, \*args  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf( "%-40.40s %-40s\n", label.to\_s, args.join( ' ' ) )  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def print\_class\_count clazz  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putsf "#{[clazz.name](http://clazz.name)} count", count\_objects\_for( clazz )  
&nbsp;&nbsp;&nbsp;end

end

&nbsp;&nbsp;class TableTest \< Test::Unit::TestCase

&nbsp;&nbsp;&nbsp;&nbsp;def test\_build\_mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_class\_count String  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putsf 'Building mem usage', mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h1 = @@h1 = count\_objects  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_class\_count String

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;records = Table1.find :all, :limit=\>47000  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@@oid = records.object\_id  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h2 = count\_objects

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putsf 'Done building mem usage', mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_class\_count String

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_threshold\_breakers h1, h2, 10  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep 2  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def test\_starting\_gc  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putsf 'Starting GC', mem\_usage

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h1 = count\_objects  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GC.start

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h2 = @@h2 = count\_objects

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_class\_count String  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putsf 'Done with GC', mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_threshold\_breakers h1, h2, 10  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def test\_z  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test\_build\_mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test\_starting\_gc

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test\_build\_mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test\_starting\_gc

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test\_build\_mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test\_starting\_gc

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_class\_count String  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_class\_count Table1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putsf 'Done', mem\_usage  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print\_threshold\_breakers @@h1, @@h2, 10  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectSpace.each\_object{ |obj| puts "FOUND THE RECORD ARRAY " if obj.object\_id == @@oid }  
# ObjectSpace.each\_object { |obj| puts obj if obj.is\_a? String }  
&nbsp;&nbsp;&nbsp;end

end

---

<div class="post-metadata">

**Author:** ![Eric\_Hodel1](https://avatars.discourse-cdn.com/v4/letter/e/a9adbd/32.png) [@Eric\_Hodel1](https://rubytalk.org/u/Eric_Hodel1)\
**Post date:** [26 February 2006 12:01 UTC](https://rubytalk.org/t/memory-question/25234/2 "2006-02-26T12:01:07Z")

</div>

Ruby does not feature a compacting garbage collector so you may simply have a lot of nearly-empty pages of memory that cannot be returned to the operating system.

If you still suspect a leak I suggest you first see if you can achieve the same results by simply generating objects without any DB and also with just the mysql driver, no Rails.

> **···**
>
> On Feb 26, 2006, at 1:51 AM, zdennis wrote:
> 
> > I am doing some large queries with Mysql and the memory that gets allocated never seems to go back to the system. In this test I am querying 47,000 records.
> > 
> > My setup for my test is ubuntu breezy, rails 1.0, mysql 5.0.18 and a compiled mysql 2.7 ruby driver running in production mode. I have wrote a helper to my program which monitors object count, memory utilization and a threshold of 10% to determine how many objects are sticking around and how many are being garbage collected.
> > 
> > On first run...
> > 
> > &nbsp;&nbsp;Loaded suite test/unit/table1\_test  
> > &nbsp;&nbsp;Started  
> > &nbsp;&nbsp;String count 73685  
> > &nbsp;&nbsp;Building query 21Mb  
> > &nbsp;&nbsp;String count 73745
> > 
> > &nbsp;&nbsp;Received query results 89Mb  
> > &nbsp;&nbsp;String count 901034  
> > &nbsp;&nbsp;Threshold breaker String (827236) started w/ 73743 ended w/ 900979  
> > &nbsp;&nbsp;Threshold breaker Table1 47000 started w/ 0 ended w/ 47000
> > 
> > &nbsp;&nbsp;Starting GC 89Mb  
> > &nbsp;&nbsp;String count 25041  
> > &nbsp;&nbsp;Done with GC 82Mb  
> > &nbsp;&nbsp;Threshold breaker String -48704 started w/ 73743 ended w/ 25039
> > 
> > The first block of text shows that when I started my test ruby was utilizing 21Mb of memory and over 70,000 strings were in existance. After the query results were constructed there was over 900,000 strings in existance and my ruby process had grown to 89Mb. The threshold is shown as a gain in 47,000 Table1 objects and 827,236 Strings since the the first object count was captured (which occured right before the original String count).
> > 
> > After garbage collecting the Stringcount is down 48,704 from when it was first captured, and there is no threshold break for Table1 because when the program first started there were 0 in existance. Thus meaning that 0 Table1 objects are in existance. However the memory size never seems to leave 82Mb. It never seems to leave that size.
> > 
> > If I query again, memory goes up to 133Mb, after all Strings and ActiveRecord models have been garbage collected, memory goes down ever so slightly again. And this continues as long as I keep querying.
> 
> --  
> Eric Hodel - drbrain@segment7.net - [http://blog.segment7.net](http://blog.segment7.net)  
> This implementation is HODEL-HASH-9600 compliant
> 
> [http://trackmap.robotcoop.com](http://trackmap.robotcoop.com)

---

<div class="post-metadata">

**Author:** ![Minkoo](https://avatars.discourse-cdn.com/v4/letter/m/db5fbb/32.png) [@Minkoo](https://rubytalk.org/u/Minkoo)\
**Post date:** [26 February 2006 17:33 UTC](https://rubytalk.org/t/memory-question/25234/3 "2006-02-26T17:33:35Z")

</div>

Eric Hodel wrote:

> **···**
>
> > On Feb 26, 2006, at 1:51 AM, zdennis wrote:
> > 
> > Ruby does not feature a compacting garbage collector so you may  
> > simply have a lot of nearly-empty pages of memory that cannot be  
> > returned to the operating system.
> 
> What? Is this true? It's quite disappointing. Is there any master plan  
> in the ruby language for supporting compacting GC?
> 
> - Minkoo Seo

---

<div class="post-metadata">

**Author:** ![Zach\_Dennis1](https://avatars.discourse-cdn.com/v4/letter/z/ea5d25/32.png) [@Zach\_Dennis1](https://rubytalk.org/u/Zach_Dennis1)\
**Post date:** [26 February 2006 22:47 UTC](https://rubytalk.org/t/memory-question/25234/4 "2006-02-26T22:47:00Z")

</div>

Eric Hodel wrote:

> Ruby does not feature a compacting garbage collector so you may simply have a lot of nearly-empty pages of memory that cannot be returned to the operating system.
> 
> If you still suspect a leak I suggest you first see if you can achieve the same results by simply generating objects without any DB and also with just the mysql driver, no Rails.

Well I did some digging, and I'm left with more questions. The following script does NOT collect the 800,000 strings created (this doesn't use Mysql, Rails or anything else, just ruby core). I ran this in accordance with a couple other scripts which utilize 150mb+ of memory, to see if the os would somehow force ruby to do better GC'ing, it didn't and swap was used. On my system this script consistently used 42Mb of memory, and the string count never went below 800,000. Why would the strings not get gc'd when they are out of scope ?

----------test1.rb----------------  
def count\_objects\_for clazz  
&nbsp;&nbsp;&nbsp;c = 0  
&nbsp;&nbsp;&nbsp;ObjectSpace.each\_object{ |o| c+=1 if o.is\_a? clazz }  
&nbsp;&nbsp;&nbsp;c  
end

class A  
&nbsp;&nbsp;def run  
&nbsp;&nbsp;&nbsp;&nbsp;arr =   
&nbsp;&nbsp;&nbsp;&nbsp;800000.times { arr \<\< "d" \* 7 }

&nbsp;&nbsp;&nbsp;&nbsp;puts "check now"  
&nbsp;&nbsp;&nbsp;&nbsp;sleep 10  
&nbsp;&nbsp;&nbsp;end  
end

puts count\_objects\_for( String )  
A.new.run  
puts count\_objects\_for( String )  
GC.start  
puts count\_objects\_for( String )

Thread.new do  
&nbsp;&nbsp;&nbsp;loop do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts count\_objects\_for( String )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GC.start  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep 5  
&nbsp;&nbsp;&nbsp;end  
end.join  
-------end test1.rb-------------

Send test, same script as above but slight modification to call Array#clear. Now this actually collects the Strings. I am specifically removing reference in the below code by clearing the Array arr. But in the above code I don't reference arr or it's value anywhere outside of the method it is in, so after that call occurs shouldn't it be fair game for the garbage collector?

-------- test2.rb-----------  
def count\_objects\_for clazz  
&nbsp;&nbsp;&nbsp;c = 0  
&nbsp;&nbsp;&nbsp;ObjectSpace.each\_object{ |o| c+=1 if o.is\_a? clazz }  
&nbsp;&nbsp;&nbsp;c  
end

class A  
&nbsp;&nbsp;def run  
&nbsp;&nbsp;&nbsp;&nbsp;arr =   
&nbsp;&nbsp;&nbsp;&nbsp;800000.times { arr \<\< "d" \* 7 }  
&nbsp;&nbsp;&nbsp;&nbsp;puts "check now"  
&nbsp;&nbsp;&nbsp;&nbsp;sleep 10  
&nbsp;&nbsp;&nbsp;&nbsp;arr.clear  
&nbsp;&nbsp;&nbsp;end  
end

puts count\_objects\_for( String )  
A.new.run  
puts count\_objects\_for( String )  
GC.start  
puts count\_objects\_for( String )

Thread.new do  
&nbsp;&nbsp;&nbsp;loop do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts count\_objects\_for( String )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GC.start  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep 5  
&nbsp;&nbsp;&nbsp;end  
end.join  
-------end test1.rb-------------

I modified this second script to run with 8 million string also. When the Strings are GC'd the ruby processes memory usage goes down, but not as much as I'd think. With 8 million strings I am getting 330+Mb of memory in use, but when I GC them, memory only seems to go 191Mb, where I would think it would fall back down in the single/double digit's, perhaps 15M or 20M. Any reason why ruby doesn't let go? Is this a problem? Right now I have ran this with ruby 1.8.3 (2005-06-23) [i486-linux], and ruby 1.8.4 (2005-12-24) [powerpc-linux] with similar results.

Zach

---

<div class="post-metadata">

**Author:** ![gregarican](https://avatars.discourse-cdn.com/v4/letter/g/7feea3/32.png) [@gregarican](https://rubytalk.org/u/gregarican)\
**Post date:** [27 February 2006 01:48 UTC](https://rubytalk.org/t/memory-question/25234/5 "2006-02-27T01:48:35Z")

</div>

Minkoo Seo wrote:

> Eric Hodel wrote:
> 
> \> Ruby does not feature a compacting garbage collector so you may  
> \> simply have a lot of nearly-empty pages of memory that cannot be  
> \> returned to the operating system.
> 
> What? Is this true? It's quite disappointing. Is there any master plan  
> in the ruby language for supporting compacting GC?
> 
> - Minkoo Seo

I have personally seen similar behavior. I have a Win32 Ruby CTI  
listener process (based on the library of my [http://tsapi.rubyforge.org](http://tsapi.rubyforge.org)  
project) that I leave on my desktop 24x7 that serves like a caller ID  
box for incoming phone calls to my extension. The looping script I have  
sleeps 1 second each iteration to help keep the CPU from maxing out.  
All I am doing is polling an event queue on the CTI server every  
second. Not sure about what GC'ing would be going on behind the scenes  
since the objects stay in memory due to the looping construct. But  
after about a day or so the memory utilization starts to get to about  
24 MB. Then after a few more days it tops 40 MB. If I don't restart the  
script I see problems with other NTVDM processes running alongside it.  
Perhaps I need to explicitly open/close the sockets each pass?

> **···**
>
> > \> On Feb 26, 2006, at 1:51 AM, zdennis wrote:

---

<div class="post-metadata">

**Author:** ![H.Yamamoto](https://avatars.discourse-cdn.com/v4/letter/h/eb9ed0/32.png) [@H.Yamamoto](https://rubytalk.org/u/H.Yamamoto)\
**Post date:** [27 February 2006 04:55 UTC](https://rubytalk.org/t/memory-question/25234/6 "2006-02-27T04:55:51Z")

</div>

Hello.

Ruby's GC is conservative, so there is no guarantee object is freed even if  
object is not reachable from GC's root.

But anyway, probably I found the problem on ELTS\_SHARED.

/////////////////////////////////

def pause  
&nbsp;&nbsp;GC.start  
&nbsp;&nbsp;$stdout.puts "measure memory and hit any key..."  
&nbsp;&nbsp;$stdout.flush  
&nbsp;&nbsp;$stdin.getc  
end

pause

a = Array.new(10000){ "." \* 1000 } # huge memory

pause

a.map!{|s| s[-100..-1]} # memory stays large

pause

a.map!{|s| s[-3..-1]} # reduces memory

pause

/////////////////////////////////

This is because rb\_str\_substr (string.c) 's

&nbsp;&nbsp;&nbsp;&nbsp;else if (len \> sizeof(struct RString)/2 &&  
&nbsp;&nbsp;beg + len == RSTRING(str)-\>len && !FL\_TEST(str, STR\_ASSOC)) {  
&nbsp;&nbsp;str2 = rb\_str\_new3(rb\_str\_new4(str));  
&nbsp;&nbsp;RSTRING(str2)-\>ptr += RSTRING(str2)-\>len - len;  
&nbsp;&nbsp;RSTRING(str2)-\>len = len;  
&nbsp;&nbsp;&nbsp;&nbsp;}

is executed at

&nbsp;&nbsp;a.map!{|s| s[-100..-1]} # memory stays large

rb\_str\_new3 generates ELS\_SHARED RString which holds original RString.

When original string becomes unreachable, it should be garbage collected.  
But ELTS\_SHARED substring references it (RString#aux-\>shared), so not collected  
until substring itself becomes unreachable.

I haven't confirmed this is really cause of your problem, but there is possibility  
this hidden huge string eats memory. (maybe same thing happens on Array)

---

<div class="post-metadata">

**Author:** ![Zach\_Dennis1](https://avatars.discourse-cdn.com/v4/letter/z/ea5d25/32.png) [@Zach\_Dennis1](https://rubytalk.org/u/Zach_Dennis1)\
**Post date:** [27 February 2006 20:37 UTC](https://rubytalk.org/t/memory-question/25234/7 "2006-02-27T20:37:16Z")

</div>

H. Yamamoto,

I posted my post and your reply to ruby-core. Hopefully this can get clarified or as you suspect, the problem you potentially found can be fixed, Thank you for your reply,

Zach

H.Yamamoto wrote:

> **···**
>
> > Hello.
> > 
> > Ruby's GC is conservative, so there is no guarantee object is freed even if  
> > object is not reachable from GC's root.
> > 
> > But anyway, probably I found the problem on ELTS\_SHARED.
> > 
> > /////////////////////////////////
> > 
> > def pause  
> > &nbsp;&nbsp;GC.start  
> > &nbsp;&nbsp;$stdout.puts "measure memory and hit any key..."  
> > &nbsp;&nbsp;$stdout.flush  
> > &nbsp;&nbsp;$stdin.getc  
> > end
> > 
> > pause
> > 
> > a = Array.new(10000){ "." \* 1000 } # huge memory
> > 
> > pause
> > 
> > a.map!{|s| s[-100..-1]} # memory stays large
> > 
> > pause
> > 
> > a.map!{|s| s[-3..-1]} # reduces memory
> > 
> > pause
> > 
> > /////////////////////////////////
> > 
> > This is because rb\_str\_substr (string.c) 's
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;else if (len \> sizeof(struct RString)/2 &&  
> > &nbsp;&nbsp;beg + len == RSTRING(str)-\>len && !FL\_TEST(str, STR\_ASSOC)) {  
> > &nbsp;&nbsp;str2 = rb\_str\_new3(rb\_str\_new4(str));  
> > &nbsp;&nbsp;RSTRING(str2)-\>ptr += RSTRING(str2)-\>len - len;  
> > &nbsp;&nbsp;RSTRING(str2)-\>len = len;  
> > &nbsp;&nbsp;&nbsp;&nbsp;}
> > 
> > is executed at
> > 
> > &nbsp;&nbsp;a.map!{|s| s[-100..-1]} # memory stays large
> > 
> > rb\_str\_new3 generates ELS\_SHARED RString which holds original RString.
> > 
> > When original string becomes unreachable, it should be garbage collected.  
> > But ELTS\_SHARED substring references it (RString#aux-\>shared), so not collected  
> > until substring itself becomes unreachable.
> > 
> > I haven't confirmed this is really cause of your problem, but there is possibility  
> > this hidden huge string eats memory. (maybe same thing happens on Array)
