Collecting garbage

  • Thread starter Eustaquio Rangel de Oliveira Jr.
  • Start date
E

Eustaquio Rangel de Oliveira Jr.

Hi.

I have a doubt about garbage collection:

-------------------------------------------------------------------------=
---------------
class CustomObject
attr_accessor :val, :next
def initialize(v,n=3Dnil)
@val =3D v
@next =3D n
end
def to_s
"Object #{@val} (#{self.object_id}) points to #{@next.nil? ? 'nothing' =
:=20
@next.val} (#{@next.nil? ? '':mad:next.object_id})"
end
end

def list
print "Listing all CustomObject's with ObjectSpace\n"
print "#{ObjectSpace.each_object(CustomObject) {|v| puts v}} objects=20
found\n\n"
end

c1 =3D CustomObject.new("1",CustomObject.new("2",CustomObject.new("3")))
c4 =3D CustomObject.new("4",CustomObject.new("5"))
c6 =3D CustomObject.new("6")
c1.next.next.next =3D c1 # comment this and check again
list

c1 =3D nil
c4.next =3D nil

GC.start # here I want c1 disappears
sleep(1)
list
-------------------------------------------------------------------------=
---------------

running this program I get

-------------------------------------------------------------------------=
---------------
Listing all CustomObject's with ObjectSpace
Object 6 (-604874926) points to nothing ()
Object 4 (-604874906) points to 5 (-604874896)
Object 5 (-604874896) points to nothing ()
Object 1 (-604874866) points to 2 (-604874856)
Object 2 (-604874856) points to 3 (-604874846)
Object 3 (-604874846) points to 1 (-604874866)
6 objects found

Listing all CustomObject's with ObjectSpace
Object 6 (-604874926) points to nothing ()
Object 4 (-604874906) points to nothing ()
Object 1 (-604874866) points to 2 (-604874856)
Object 2 (-604874856) points to 3 (-604874846)
Object 3 (-604874846) points to 1 (-604874866)
5 objects found
-------------------------------------------------------------------------=
---------------

There's a circular reference there: c1 points to 2 (created internally on=
=20
1), 2 points to 3 (created internally on 2), and 3 points to c1, which is=
a=20
reference at root (?) level there and again points to an internal 2, that=
=20
points to 3, that points to c1 ...

My question is, when assigning nil to c1, it should not invalidate all th=
e=20
inner objects and becomes available for garbage collection?
On the example above, I assigned nil to c1 and c4.next (5), and after the=
=20
GC.start I don't have 5, but still have c1 (same id), 2 and 3. On that=20
case, that memory will never be sweeped (free)? Because seems that I'll=20
always have a Object with id -604874856 and will not have a way to refer =
to=20
it later, for use or free the allocated memory for it.

Thanks.
 
Y

Yohanes Santoso

Eustaquio Rangel de Oliveira Jr. said:
My question is, when assigning nil to c1, it should not invalidate all
the inner objects and becomes available for garbage collection?
On the example above, I assigned nil to c1 and c4.next (5), and after
the GC.start I don't have 5, but still have c1 (same id), 2 and 3. On
that case, that memory will never be sweeped (free)? Because seems
that I'll always have a Object with id -604874856 and will not have a
way to refer to it later, for use or free the allocated memory for it.

Ruby's GC is lazy and conservative. Lazy meaning, it does not
exhaustively try to do path-finding on each object because for some
objects, path-finding is an expensive process. Conservative meaning,
if it is not doing a path-finding on an object, that object is not
freed.

Hate it or love it. It has both advantages and disadvantages. You have
just shown a disadvantage. But it can be mitigated by exiting the
scope which should make path-finding to those objects cheaper.



class CustomObject
attr_accessor :val, :next
def initialize(v,n=nil)
@val = v
@next = n
end
def to_s
"Object #{@val} (#{self.object_id}) points to #{@next.nil? ? 'nothing' : @next.val} (#{@next.nil? ? '':mad:next.object_id})"
end
end

def list
print "Listing all CustomObject's with ObjectSpace\n"
print "#{ObjectSpace.each_object(CustomObject) {|v| puts v}} objects found\n\n"
end

begin # start a new scope so we can exit it later
c1 = CustomObject.new(1,CustomObject.new(2,CustomObject.new(3)))
c4 = CustomObject.new(4,CustomObject.new(5))
c6 = CustomObject.new(6)
c1.next.next.next = c1 # comment this and check again
puts "### Initial"
list

c1 = nil
c4.next = nil

GC.start

puts "### After gc, but still within declaring scope"
list
end

puts "### Exitted the scope"
list

GC.start # here I want c1 disappears

puts "### After gc, outside of declaring scope"
list




Output:

### Initial
Listing all CustomObject's with ObjectSpace
Object 6 (-604561942) points to nothing ()
Object 4 (-604561932) points to 5 (-604561922)
Object 5 (-604561922) points to nothing ()
Object 1 (-604561912) points to 2 (-604561902)
Object 2 (-604561902) points to 3 (-604561892)
Object 3 (-604561892) points to 1 (-604561912)
6 objects found

### After gc, but still within declaring scope
Listing all CustomObject's with ObjectSpace
Object 6 (-604561942) points to nothing ()
Object 4 (-604561932) points to nothing ()
Object 1 (-604561912) points to 2 (-604561902)
Object 2 (-604561902) points to 3 (-604561892)
Object 3 (-604561892) points to 1 (-604561912)
5 objects found

### Exitted the scope
Listing all CustomObject's with ObjectSpace
Object 6 (-604561942) points to nothing ()
Object 4 (-604561932) points to nothing ()
Object 1 (-604561912) points to 2 (-604561902)
Object 2 (-604561902) points to 3 (-604561892)
Object 3 (-604561892) points to 1 (-604561912)
5 objects found

### After gc, outside of declaring scope
Listing all CustomObject's with ObjectSpace
Object 6 (-604561942) points to nothing ()
Object 4 (-604561932) points to nothing ()
2 objects found
 
Y

Yohanes Santoso

Yohanes Santoso said:
exiting the scope which should make path-finding to those objects
cheaper.

correction: declaring scope, not variable scope.

YS.
 
E

Eustaquio Rangel de Oliveira Jr.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi!

Thanks for the answer. :)

| Ruby's GC is lazy and conservative. Lazy meaning, it does not
| exhaustively try to do path-finding on each object because for some
| objects, path-finding is an expensive process. Conservative meaning,
| if it is not doing a path-finding on an object, that object is not
| freed.
|
| Hate it or love it. It has both advantages and disadvantages. You have
| just shown a disadvantage. But it can be mitigated by exiting the
| scope which should make path-finding to those objects cheaper.

I'll search about lazy and conversative gc's. :)
Just another question: if I use a method there, and not a begin/end block,
like this

- --------------------------------------------------------------------------
def test
c1 = CustomObject.new("1",CustomObject.new("2",CustomObject.new("3")))
c4 = CustomObject.new("4",CustomObject.new("5"))
c6 = CustomObject.new("6")
c1.next.next.next = c1 # comment this and check again
list
end

test
GC.start
sleep(1)
list
- --------------------------------------------------------------------------

After run this, only c6 remains. It's about scope also?

Thanks!

- ----------------------------
Eust?quio "TaQ" Rangel
(e-mail address removed)
http://beam.to/taq
Usu?rio GNU/Linux no. 224050
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.7 (GNU/Linux)

iD8DBQFCzQuLb6UiZnhJiLsRAq8mAKCZdk3XkCidxwRLhxKG7unSrBIdEQCeNkMh
Im7s5zjG8umPPDkrXwwgCk0=
=2bZr
-----END PGP SIGNATURE-----
 
Y

Yohanes Santoso

Eustaquio Rangel de Oliveira Jr. said:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi!

Thanks for the answer. :)

| Ruby's GC is lazy and conservative. Lazy meaning, it does not
| exhaustively try to do path-finding on each object because for some
| objects, path-finding is an expensive process. Conservative meaning,
| if it is not doing a path-finding on an object, that object is not
| freed.
|
| Hate it or love it. It has both advantages and disadvantages. You have
| just shown a disadvantage. But it can be mitigated by exiting the
| scope which should make path-finding to those objects cheaper.

I'll search about lazy and conversative gc's. :)
Just another question: if I use a method there, and not a begin/end block,
like this

- --------------------------------------------------------------------------
def test
c1 = CustomObject.new("1",CustomObject.new("2",CustomObject.new("3")))
c4 = CustomObject.new("4",CustomObject.new("5"))
c6 = CustomObject.new("6")
c1.next.next.next = c1 # comment this and check again
list
end

test
GC.start
sleep(1)
list
- --------------------------------------------------------------------------

After run this, only c6 remains. It's about scope also?

Funny, I couldn't reproduce what you are seeing. I cut and paste the
code you have and run it:

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
/tmp $ ruby1.8 ./gc.rb
Listing all CustomObject's with ObjectSpace
Object 6 (-604561642) points to nothing ()
Object 4 (-604561622) points to 5 (-604561612)
Object 5 (-604561612) points to nothing ()
Object 1 (-604561582) points to 2 (-604561572)
Object 2 (-604561572) points to 3 (-604561562)
Object 3 (-604561562) points to 1 (-604561582)
6 objects found

Listing all CustomObject's with ObjectSpace
0 objects found
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Nothing remains.

Probably you are seeing c6 remaining because you were playing with
your code like so:

vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
def test
c1 = CustomObject.new("1",CustomObject.new("2",CustomObject.new("3")))
c4 = CustomObject.new("4",CustomObject.new("5"))
c6 = CustomObject.new("6")
end

test
GC.start
sleep(1)
list
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Notice that c6 is returned from test, so the object refered to by c6
is now accessible from where you invoke GC.start and that is the
original problem.


YS.
 
E

Eustaquio Rangel de Oliveira Jr.

Hi!
Probably you are seeing c6 remaining because you were playing with
your code like so:=20

Not really, let me paste the full code here:
-------------------------------------------------------------------------=
------
class CustomObject
attr_accessor :val, :next
def initialize(v,n=3Dnil)
@Val =3D v
@next =3D n
end
def to_s
"Object #{@val} (#{self.object_id}) points to #{@next.nil? ? 'nothing' =
:=20
@next.val} (#{@next.nil? ? '':mad:next.object_id})"
end
end

def list
print "Listing all the CustomObject's with ObjectSpace\n"
print "#{ObjectSpace.each_object(CustomObject) {|v| puts v}} objects=20
found\n\n"
end

def test
c1 =3D CustomObject.new("1",CustomObject.new("2",CustomObject.new("3")))
c4 =3D CustomObject.new("4",CustomObject.new("5"))
c6 =3D CustomObject.new("6")
c1.next.next.next =3D c1=09
list

c1 =3D nil
c4.next =3D nil
end

test
GC.start =09
sleep(1)
list
-------------------------------------------------------------------------=
------
running
-------------------------------------------------------------------------=
------
Listing all the CustomObject's with ObjectSpace
Object 6 (-604875086) points to nothing ()
Object 4 (-604875066) points to 5 (-604875056)
Object 5 (-604875056) points to nothing ()
Object 1 (-604875026) points to 2 (-604875016)
Object 2 (-604875016) points to 3 (-604875006)
Object 3 (-604875006) points to 1 (-604875026)
6 objects found

Listing all the CustomObject's with ObjectSpace
Object 6 (-604875086) points to nothing ()
1 objects found
-------------------------------------------------------------------------=
------
Funny, I couldn't reproduce what you are seeing.
Nothing remains.

Maybe a situation like this:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/142834

But the main point of this question you solved: the begin ... end works=20
perfectly.

Thanks!
 
Y

Yohanes Santoso

Eustaquio Rangel de Oliveira Jr. said:
Not really, let me paste the full code here:
Listing all the CustomObject's with ObjectSpace
Object 6 (-604875086) points to nothing ()
1 objects found

Still same thing, I got 0 objects found at the end.

But I'm glad you have satisfied your curiosity.

YS.
 
J

Jay Cotton

I've been following this thread as well as why's post (and the
comments) at:

http://redhanded.hobix.com/inspect/stuffingYourHandDownTheDisposal.html

and I'm left confused about 2 things:

1) What sort of scope does begin..end introduce? In the comments to
his post, why writes "Variables declared inside a begin..end or
inside a block are block local, they perish with the close of the
block." But my own experience seems to contradict why's statement.
This program does not throw an error:

#!/usr/local/bin/ruby
begin
x = 1
end
p x

2) Why are people getting different results when they run the CustObj
garbage collection code? When I run it, Object 1 never gets collected
by GC, even after exiting the begin..end scope.

Can anybody help me understand?

Jay
 
E

Eric Hodel

I've been following this thread as well as why's post (and the
comments) at:

http://redhanded.hobix.com/inspect/
stuffingYourHandDownTheDisposal.html

and I'm left confused about 2 things:

1) What sort of scope does begin..end introduce? In the comments to
his post, why writes "Variables declared inside a begin..end or
inside a block are block local, they perish with the close of the
block." But my own experience seems to contradict why's statement.
This program does not throw an error:

#!/usr/local/bin/ruby
begin
x = 1
end
p x

begin does not create a new scope. why made a mistake here.
2) Why are people getting different results when they run the
CustObj garbage collection code? When I run it, Object 1 never gets
collected by GC, even after exiting the begin..end scope.

Can anybody help me understand?

The GC is very finicky, so you may or may not get the same behavior
as other people do. That's part of the nature of Ruby's conservative
GC.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top