more gc woes

Posted by jmls on 19-Apr-2012 03:45

having more issues with the auto gc

class class1:

  def var c2 as class2 no-undo.

  constructor class1():

    c2 = new class2().

    c2:foo:Subscribe(foo).

  end constructor.

  destructor class1():

    message "delete c1" view-as alert-box.

  end destructor.

  method private void foo():

  end method.

end class.

using Progress.Lang.*.

class class2:

  def public event foo signature void ().

  constructor class2():

  end constructor.

  destructor class2():

    message "delete c2" view-as alert-box.

  end destructor. 

end class.

run this code

(NEW class1()).

MESSAGE "here" VIEW-AS ALERT-BOX.

you will see that the objects are not deleted until after the "here" message.

I presume that this is because class1 has subscribed to an event in class2 (and therefore class2 has some reference to class1)

Couldn't this create a load of memory leak issues ?

All Replies

Posted by Admin on 19-Apr-2012 03:57

I presume that this is because class1 has subscribed to an event in class2 (and therefore class2 has some reference to class1)

 

You need to understand, that when class1 subscribes to an event in class2, class1 passes a reference of itself to class2 as a subscriber.

Posted by jmls on 19-Apr-2012 04:37

yeah, that much I figured

Posted by Peter Judge on 19-Apr-2012 06:57

Would also just note that if you're depending on GC firing at a certain time, you're doing it wrong. The algorithm used to decide when to perform GC is not documented and is subject to change.

If you have a dependency on some destructor behaviour, either manually delete the object, or move that behaviour into another method - AntiInitialize() - and call it when you need the behaviour performed.

-- peter

Posted by Admin on 19-Apr-2012 07:07

another method - AntiInitialize

By us .NETties aka Dispose().

Posted by jmls on 19-Apr-2012 07:26

I am aware that there is no certain time - that's no the problem.

The problem with this code is that the GC *never* kicks in for these objects. So in this case, "no certain time" == never. Which is a bad thing because of the huge memory leaks it will cause.

Posted by jmls on 19-Apr-2012 07:31

pjudge wrote:

If you have a dependency on some destructor behaviour, either manually delete the object, or move that behaviour into another method - AntiInitialize() - and call it when you need the behaviour performed.

-- peter

all of the above cannot be done in this code :

(NEW class1()).

which means that I now have to do

def var a as class1.

a = new class1().

/** do stuff */

delete object a.

yuk.

*Lightbulb* hmm. this may work ...

delete object (new class1()).

(kinda like the wtf aspect of this)

or

(new class1()):dispose(). /** where dispose is a method that delete's this-object */

 * jmls goes off to experiment

Posted by Admin on 19-Apr-2012 07:32

Which is a bad thing because of the huge memory leaks it will cause.

You need to be aware of the reference count consequences coming from event subscriptions and start generating your Dispose() methods (or AntiInitialize()).

Posted by Admin on 19-Apr-2012 07:37

(new class1()):dispose(). /** where dispose is a method that delete's this-object */

 

Not exactly...

Dispose() should unsubscribe the event. That should be enough. I'm not a fan of objects committing "suicide".

Posted by jmls on 19-Apr-2012 07:57

trust me, I am well aware now

Posted by Thomas Mercer-Hursh on 19-Apr-2012 11:24

I have to say that I am suspicious of the use case here.  It smells a bit like shooting oneself in the foot because of doing something that one shouldn't have done in the first place and then wondering why there is no AutoBandage function.

Posted by jmls on 20-Apr-2012 01:07

tamhas wrote:

I have to say that I am suspicious of the use case here.  It smells a bit like shooting oneself in the foot because of doing something that one shouldn't have done in the first place and then wondering why there is no AutoBandage function.

rubbish. your sense of smell is broken.

What you are implying is that we should *never* rely on automatic GC and expect us to completely manage the object life cycle manually.

why is the statement  new SomeObject():foo()  "doing something that one shouldn't have done in the first place"  ?

This is common use in both fluent programming, in .net components .

Posted by Thomas Mercer-Hursh on 20-Apr-2012 11:25

First, about garbage collection ... there are at least two almost polar opposite attitudes about it.  One is that one should manage the lifecycle of one's objects so that, if one is doing things right, automatic garbage collection almost never happens.  There are, of course, some cases where it is convenient and tidier to rely on garbage collection in order to avoid the reference tracking which would otherwise be needed, e.g., my 10.1 version of singletons.  The other view seems to be that garbage collection is there to make life easy and one should never have to bother about cleaning up after oneself since the clean up will happen naturally when one is done with something.  I tend to the former.

As for new someObject():foo(), while it is true that I am not particularly fond of the fluent style, that is NOT the actual problem here.  I don't know what the actual problem IS because I don't know your use case, but I am suspicious.   I am suspicious particularly if that one line is the only reference to someObject.  That makes me think that someObject is just a wrapped up function that should actually be part of some larger subject matter.  And, were it part of a larger subject matter, I suspect that your life cycle issues would go away.

Posted by jmls on 20-Apr-2012 11:40

I do both

I delete the objects if I feel a need to make sure that they are not hanging around for a bit.

I also relay on the GC to remove objects that I don't need to reference

The code behind SomeObject():foo() is a mechanism for sending a message to an external message broker.

SomeObject() is the class that deals with setting up the connection, foo() is the method to send the actual message.

in this case, the message being sent is a single one-off message, so there is no need to keep the connection open, hence the

new CreateAConnection():AndNowSendThisMessage("blah").

I think this looks much cleaner than

def var Connection1 as CreateAConnection no-undo.

Connection1 = new CreateAConnection().

Connection1:AndNowSendThisMessage("blah").

delete object Connection1.

this is nothing to do with object patterns, or design, or ThisIsHowToDoIt (jmls) , YouShouldNeverDoItThatWay (tmh).

It just *looks* better. GC to the rescue for cleaning up automatically.

However, because of the event subscription in Connection1 (because we can't have read-response methods for sockets so I have to have an event) the GC now doesn't kick in ... that was my problem.

Posted by Thomas Mercer-Hursh on 20-Apr-2012 11:56

And I would suggest that "doing both" is an invitation to trouble.

Yes, for that specific example, it is "cleaner", but as this thread indicates, the surface "cleanliness" may conceal complications which would not be there with a less "elegant" expression.

But, more to the point, do you actually think it is good practice to spend the effort required to instantiate an object for the purpose of sending a single message?  Why not a persistant message handler that only needs to be instantiated once?

Posted by jmls on 20-Apr-2012 12:06

tamhas wrote:

And I would suggest that "doing both" is an invitation to trouble.

of course you would . I would expect nothing less. More's the pity the cagematch was cancelled

tamhas wrote:

But, more to the point, do you actually think it is good practice to spend the effort required to instantiate an object for the purpose of sending a single message?  Why not a persistant message handler that only needs to be instantiated once?

for this use-case, yes. As the broker has cleanup routines which disconnect any "idle" clients. So, I would then have to write heartbeat routines, check for connected, and try to reconnect if it all went pear-shaped. Progress is *not* very good when trying to connect a socket to a non-responsive port (aka it takes some time, you can't trap the error etc etc)

I had these problems *before* I changed over to this mechanism. The couple of ms it takes to instantiate and connect is much much better time spent than continuously checking for errors etc.

YMMV. I know it will.

This thread is closed