Singleton and garbage collection

I

Ivan Fomichev

Hi, folks,

I've encountered a garbage collection problem. It seems, that perl
destroys an object in spite of an existing reference. I'm using perl
v5.8.4. Here is a piece of code:

use strict;
package Singleton;
use Data::Dumper;
my $self;
sub new {
print +(caller(0))[3], "\n";
my ($class) = shift;
return $self if $self;

my $item = Item->new();
$self = bless \$item => $class;
print Dumper($self);
return $self;
}
sub DESTROY {
print +(caller(0))[3], "\n";
print Dumper(shift);
}
package Item;
sub new {
print +(caller(0))[3], "\n";
return bless \(my $self = "value") => shift;
}
sub DESTROY {
print +(caller(0))[3], "\n";
}
package main;
my $s = Singleton->new();
__END__

Output is:

Singleton::new
Item::new
$VAR1 = bless( do{\(my $o = bless( do{\(my $o = 'value')}, 'Item' ))},
'Singleton' );
Item::DESTROY
Singleton::DESTROY
$VAR1 = bless( do{\(my $o = undef)}, 'Singleton' );

Where is the problem? There is no circular references of whatever, so
why does perl do, what it does? Is it a bug? Is there any workaround?
Do you have any ideas?

If it were not a singleton, everything worked fine, but a singleton is
the very thing I need.

use strict;
package Object;
use Data::Dumper;
sub new {
print +(caller(0))[3], "\n";
my ($class) = shift;

my $item = Item->new();
my $self = bless \$item => $class;
print Dumper($self);
return $self;
}
sub DESTROY {
print +(caller(0))[3], "\n";
print Dumper(shift);
}
package Item;
sub new {
print +(caller(0))[3], "\n";
return bless \(my $self = "value") => shift;
}
sub DESTROY {
print +(caller(0))[3], "\n";
}
package main;
my $o = Object->new();
__END__

And here is a reasonable output:

Object::new
Item::new
$VAR1 = bless( do{\(my $o = bless( do{\(my $o = 'value')}, 'Item' ))},
'Object' );
Object::DESTROY
$VAR1 = bless( do{\(my $o = bless( do{\(my $o = 'value')}, 'Item' ))},
'Object' );
Item::DESTROY
 
J

jl_post

Ivan said:
I've encountered a garbage collection problem. It seems, that perl
destroys an object in spite of an existing reference.

Where is the problem? There is no circular references of whatever, so
why does perl do, what it does? Is it a bug? Is there any workaround?
Do you have any ideas?


For one thing, even a singleton gets destroyed when a program ends
(why shouldn't it?). To explain a little better, try inserting this
line of code as the last line of your singleton script (right after you
declare $s and right before the __END__ tag):

my $s2 = Singleton->new();

Now run the program. You'll notice that Singleton::DESTROY() only
gets called once, despite the fact that Singleton->new() got called
twice. In other words, only one object of the Singleton class was
created, so only one was DESTROY()ed.

Try adding this similar line:

my $o2 = Object->new();

to the end of your second program (the one that doesn't use singletons)
and you'll see that Object::DESTROY() gets called twice.

I think your mistake was in thinking that singletons never get
destroyed. They do -- but generally when programs terminate. It's
just that singletons still maintain the right to get destroyed once.

I hope this clears things up, Ivan.

-- Jean-Luc Romano
 
I

Ivan Fomichev

Hello, Jean-Luc,

It's not a secret for me, that singletons are destroyed like any other
objects. The problem I reported was that an Item instance is destroyed
before a Singleton instance, which contains a reference to it.

Sorry for an inaccurate question.
 
J

jl_post

Ivan said:
It's not a secret for me, that singletons are destroyed like any other
objects. The problem I reported was that an Item instance is destroyed
before a Singleton instance, which contains a reference to it.


Er... it's possible I don't understand your question. Shouldn't any
and all objects contained in the Singleton class be destroyed before
the Singleton class itself gets destroyed (just like any other object)?

Looking at your output:

Item::DESTROY
Singleton::DESTROY

This shows that the Item object gets destroyed first (before the
Singleton object). I don't see why this is a problem, partly because
the Item object isn't a Singleton object (unlike the Singleton
package). In other words, there's nothing stopping you from having
more than Item instantiation.

And if I still don't understand your problem, I hope you can figure
out what's going on anyway.

Good luck!

-- Jean-Luc
 
X

xhoster

Ivan Fomichev said:
Hello, Jean-Luc,

It's not a secret for me, that singletons are destroyed like any other
objects. The problem I reported was that an Item instance is destroyed
before a Singleton instance, which contains a reference to it.

Sorry for an inaccurate question.

During global destruction, objects are destroyed in whatever order perl
feels like.

Xho
 
A

Anno Siegel

During global destruction, objects are destroyed in whatever order perl
feels like.

Indeed. It's *supposed* to break (circular) references in that stage.

This is really another object lesson on the subject "Write Robust
Destructors". Don't ever assume anything, you'll get called in the
weirdest situations.

Anno
 
I

Ivan Fomichev

Anno said:
Indeed. It's *supposed* to break (circular) references in that stage.

Everything's fine, but there are no circular references here, as far as
I understand my code. So I suppose, that they should be garbage
collected during reference-based garbage collection phase, not during
mark-and-sweep phase. So I expect, that Item should not be garbage
collected before Singleton, since the second keeps a reference to the
former. Please refer to perlobj. Am I wrong? Please disappoint me.
 
A

A. Sinan Unur

Everything's fine, but there are no circular references here, as far
as I understand my code. So I suppose, that they should be garbage
collected during reference-based garbage collection phase, not during
mark-and-sweep phase. So I expect, that Item should not be garbage
collected before Singleton, since the second keeps a reference to the
former. Please refer to perlobj. Am I wrong? Please disappoint me.

I am not expert, but it makes sense to me that objects are destroyed in
the order they were created.

At program end, cleaning up Singleton means also cleaning up everything
to which it holds a reference. Note that this is my interpretation based
on observed behavior, rather than any first hand knowledge of how
garbage collection works.

Sinan
 
I

Ivan Fomichev

A. Sinan Unur said:
I am not expert, but it makes sense to me that objects are destroyed in
the order they were created.
That is not true. For an easy disproof, please see sample code with
Object class above.
At program end, cleaning up Singleton means also cleaning up everything
to which it holds a reference.
Sure, but destructor for Item should be called *after* destructor for
Singleton. Item instance should not be destroyed, as long as I keep a
reference to it, because I may want to access Item data from Singleton
destructor (and that's the very thing I want).
 
A

Anno Siegel

Ivan Fomichev said:
Everything's fine, but there are no circular references here, as far as
I understand my code.

It doesn't matter if there are, in fact, circular references. You just
can't expect global destruction to respect existing references.
So I suppose, that they should be garbage
collected during reference-based garbage collection phase, not during
mark-and-sweep phase.

No, that's the difference (one of them) between the singleton scenario
and the standard class setup. With the singleton, the first thing
->new returns is stored in a global lexical. So it *must* remain until
global destruction.

I have appended a setup, similar to your non-singleton example. The
first few lines is where the action is. They are

# my $global;
# sub access_global { $global }
{
my $obj = Object->new;
# $global = $obj;
}
print "bare-block destruction done, global destruction begins\n";
exit;

If you run it as is, $obj and then its depending Item object are destroyed
when the bare block ends, before global destruction.

After activating the first and fifth lines, assigning $obj to a global
variable, $obj survives the block and is destroyed globally, but (on
my machine, with my perl) the sequence of operation is the same: First
$obj dies, then the anonymous Item object.

If you also have a sub that accesses the global variable, things change.
After activation of the second line, the sequence of destruction reverses
and Item is destroyed first, at least that's what I see on two machines.
I don't claim to understand why this is so, and it doesn't really matter.
Global destruction happens essentially at random.

Your singleton-scenario is close to the situation we have here: There
is a global variable that holds the reference, and that variable is
accessed by a sub (->new itself, in that case). Apparently you got
the same behavior. There is nothing wrong with that.

The lesson is, don't assume a particular sequence in object destruction.
In fact, in a destructor it's best not to assume anything.

Anno
 
A

Anno Siegel

I have appended a setup, similar to your non-singleton example. The

Sorry, forgot to append the complete program. Here is is:


#!/usr/bin/perl
use strict; use warnings; $| = 1; # @^~`
use Vi::QuickFix;

my $global;
# sub access_global { $global }
{
my $obj = Object->new;
$global = $obj;
}
print "bare-block destruction done, global destruction begins\n";
exit;


package Object;

sub new {
Aux::show_sub();
my $class = shift;
my $item = Item->new( 'woohoo');
bless \ $item, $class;
}

sub DESTROY {
Aux::show_sub();
}

package Item;

sub new {
Aux::show_sub();
my ( $class, $val) = @_;
bless \ $val, $class;
}


sub DESTROY {
Aux::show_sub();
}

package Aux;

sub show_sub { print +( caller( 1))[ 3], "\n" }
 
A

A. Sinan Unur

That is not true. For an easy disproof, please see sample code with
Object class above.

I had meant in the reverse order they were created. Like popping
elements from a stack.
Sure, but destructor for Item should be called *after* destructor for
Singleton. Item instance should not be destroyed, as long as I keep a
reference to it, because I may want to access Item data from Singleton
destructor (and that's the very thing I want).

During program execution time, yes. I just don't see why that should
hold when the program terminates. I don't think there is a language that
makes guarantee regarding regarding the order of object destruction
during program termination.

My newsreader automatically snips signatures if you use a proper sig
separator. That is, you need 'dash-dash-space-newline' before your sig.
 
I

Ivan Fomichev

Anno said:
The lesson is, don't assume a particular sequence in object destruction.
In fact, in a destructor it's best not to assume anything.

Thank you very much for a detailed answer, things got much clearer now.
 
I

Ivan Fomichev

I'm glad to inform, that I've found a satisfactory workaround. END can
be used instead of DESTROY. Though I am not sure, if there can be side
effects of such use.

use strict;
package Singleton;
use Data::Dumper;
my $self;
sub new {
print +(caller(0))[3], "\n";
my ($class) = shift;
return $self if $self;

my $item = Item->new();
$self = bless \$item => $class;
print Dumper($self);
return $self;
}

sub END {
print +(caller(0))[3], "\n";
print Dumper($self);
}

package Item;
sub new {
print +(caller(0))[3], "\n";
return bless \(my $self = "value") => shift;
}

sub DESTROY {
print +(caller(0))[3], "\n";
}

package main;
my $s = Singleton->new();
__END__

Output:

Singleton::new
Item::new
$VAR1 = bless( do{\(my $o = bless( do{\(my $o = 'value')}, 'Item' ))},
'Singleton' );
Singleton::END
$VAR1 = bless( do{\(my $o = bless( do{\(my $o = 'value')}, 'Item' ))},
'Singleton' );
Item::DESTROY
 
X

xhoster

Ivan Fomichev said:
Everything's fine, but there are no circular references here, as far as
I understand my code.

True, but perl wants to be ready for that event, whether it actually
happens to occur in your particular code or not.
So I suppose, that they should be garbage
collected during reference-based garbage collection phase, not during
mark-and-sweep phase.

The lexical variable $self doesn't seem to go away until the mark and sweep
phase, therefore the reference-chain it holds survive until after
reference- based phase is done.

If you add a finalize method to the singleton which does an "undef $self",
and call it just before you exit. Then you get the destruction order you
want.


Xho
 
X

xhoster

Ivan Fomichev said:
I'm glad to inform, that I've found a satisfactory workaround. END can
be used instead of DESTROY. Though I am not sure, if there can be side
effects of such use.

Yes, END is better than my suggestion of finalize, because it doesn't
need to be called explicitly. (Or maybe it is worse, because then it is
harder to manually control the order of finalization of different classes,
if that matters.)
use strict;
package Singleton;
use Data::Dumper;
my $self;
sub new {
print +(caller(0))[3], "\n";
my ($class) = shift;
return $self if $self;

my $item = Item->new();
$self = bless \$item => $class;
print Dumper($self);
return $self;
}

sub END {
print +(caller(0))[3], "\n";
print Dumper($self);
}


However, the Singleton sticks around, holding a reference to Item, so Item
is still being destroyed during global destruction in your code. If you
want Item to be destroyed during reference-based destruction, then you need
to add "undef $self" to the Singleton's END routine.

Xho
 
B

brian d foy

The lexical variable $self doesn't seem to go away until the mark and sweep
phase, therefore the reference-chain it holds survive until after
reference- based phase is done.

You can weaken (see Scalar:Util) the initial singleton variable so it
does count in the reference count. After you do that, things should
happen more like what you expect. :)
*** Free account sponsored by SecureIX.com ***
*** Encrypt your Internet usage with a free VPN account from http://www.SecureIX.com ***
 
X

xhoster

brian d foy said:
You can weaken (see Scalar:Util) the initial singleton variable so it
does count in the reference count. After you do that, things should
happen more like what you expect. :)

Well, that depends on what you expect. :)

If you do that, then it isn't really a singleton anymore, the way I see it.
At any given time, you will have only one instance, but over time you may
have different ones.

If you have:

package main;
{
my $s = Singleton->new();
}
{
my $s = Singleton->new();
}

Then the two $s will not be the same object, because it was destroyed
when the first $s went out of scope, so the "singleton" had to be recreated
for second "new".

Xho
 

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,054
Latest member
TrimKetoBoost

Latest Threads

Top