I just noticed that ABL object (custom class (myobject.cls) that were instantiated using NEW) doesn't call its destructor.
I'm expecting that when I close a window/form that the destructor will eventually be called for these ABL objects created inside the window. I don't see the call to the destructor happening unless I use DELETE OBJECT.
Is this expected for object that are already out of scope?
The most likely explanation is that there is still a reference. You might use log-manager to trace the history.
Classes w/an active pub-sub subscription link have a bidirectional link and would need to be manually deleted.
In general if the object inherits from .NET, it won't go away unless .NET garbage collects it. Though I believe that for a form, if it gets disposed (which it should be if you closed it), we will clean it up. But this won't happen until we call .NET again for something else.
This is only a problem when the subscriber is the one going out of scope and the publisher is staying. If the publisher is going away, the subscriber will go out of scope and should also be available for collection once the publisher is collected. At least, this is how .NET Framework works.
It's a plain ABL class. It doesn't inherit any .NET Framework classes.
Class A inherits Form
Define myObject as Class B
myObject = NEW B(THIS-OBJECT)
After closing A which is a window/form, I expect for A to be collected followed by myObject(B). The publisher is going away the subscriber should go away after.
Ok. But as I said. Did anything else happen after you closed the form? Is there any other code that calls into .NET?
As Tim said, pub-sub creates a circular reference and neither end is garbage collected without an explicit delete.
Here is the code;
METHOD PUBLIC STATIC VOID MyMethod( ):
DEFINE VARIABLE frm AS Class A NO-UNDO.
frm = NEW A().
That might be a bad behavior because no form will ever get collected if that is the case since you don't specifically unhook event subscription to controls and 'what nots'.
And you haven't addressed my point. Showing me that method code doesn't tell me anything. I presume that is not the end of the application. IF there are no circular references as others have suggested, after you close the form. Can you do something else that will cause the AVM to call into .NET? Something like opening another form or responding to some UI event that calls a .NET method or gets a .NET property. If you do, does the form's destructor run then?
The form I'm closing is called from a main form. I'm repeatedly opening an instance of the form and closing it. I do see the message I dropped inside the form DESTRUCTOR being called when opening another instance of the form. The message I added inside the DESTRUCTOR of the class in question is not being displayed even when closing the application.
Ok. That means forms are working as expected. So what exactly is the object you're talking about? Is it a control on the form? Have you tried forcing .NET garbage collection to see if it goes away? I'm not really recommending you do that, but it would be just to see if that's the problem. You'd have to do it after the form is closed, like in an FormClosed event handler.
Is it an Infragistics control? We know there can be all kinds of hidden references to those.
And there are tools like YourKit for .Net that will tell you if there are still references to .NET objects. You can download a trial copy of it.
It is just a plain ABL class.
Oh Gee. I thought in your very first post that you said it inherited from .NET. But you didn't. I guess I was thrown off by "I'm expecting that when I close a window/form that the destructor will eventually be called for these ABL objects created inside the window". Sorry.
It's a class for my MVC or MVP pattern. It's pretty standard. It's the presenter class in my sample below that is not being collected. As proven from my previous post, the window/form is being collected as expected, so I this is not a case of circular event reference.
Class View inherits Form
Define myPresenter as Class Presenter
myPresenter = NEW B(THIS-OBJECT)
OK.. So when you subscribe to the event in the form, the form has a reference to the Presenter class (since the form needs to call code in Presenter). When the form goes away, I would think that that reference would go away. But for diagnostic purposes, please try to UnSubscribe from the event and see if it fixes the problem. To do this, you could have the Presenter class also subscribe to FormClosed and put the unsubscribes (now 2 of them!) in the FormClosed handler.
I'm not sure what the code above is trying to tell us (using the tool to post ABL code might help, but here I think the problem is mostly that it is incomplete fragments). But, if you have two objects, one publishing and the other subscribing, then you have a circular reference. It is in the nature of the beast and has been recognized for a few years as a condition where automatic garbage collection does not happen.
myPresenter is declared inside the form class. If the form is being collected it should be safe to assume that the myPresenter should also be collected. Or else the form cannot be collected.
That is not correct. It is all about references, not about ownership. If Presenter is inside the form, form has a reference to Presenter, NOT the other way around (disregarding any subscribes for the moment). So the form can go away but still leave something that it referenced as long as something else also has a reference to it.
In this case, as I said, the form had a reference to Presenter because of the subscription (not the other way around). So the form can still go away. And as I said, this should decrement that subscription reference. But you need to try doing the unsubscribe so we can stop wondering if that is what the problem is. Just try it and see what happens.
But you're correct that there is no circular reference or the form could not have been garbage collected.
I tested two things;
1. I unsubscribed to all the events (including FormClosed) inside the FormClosed eventhandler of the Presenter
2. I commented out all the event subscriptions inside my Presenter
Both didn't result to the Presenter being collected. Only the form runs its destructor.
Also, no other objects reference the Presenter only the form.
Just to clarify, my assumption is based on what I know about the .NET Framework in which the event source (publisher - Form ) is the sole responsible for maintaining the references to the event listeners (subscriber - Presenter).
The ABL and CLR Bridge might have been implemented differently which is what I'm trying to get a confirmation of.
Well in the ABL/CLR Bridge environment, the bridge is really the subscriber and when it's handler is run it calls back to the ABL. But that should not affect the reference counting, There may be a bug, but since it sounds like such a basic scenario, I would be surprised that no one has found it yet. I don't think I can say anymore without at least looking at the code. You could get tech support involved so we can take a look at it.
Sounds like you could use logging of reference counts. I don't think we have that. But someone can correct that if I'm wrong.
[quote user="Thomas Mercer-Hursh"]
But, if you have two objects, one publishing and the other subscribing, then you have a circular reference.
That's not correct Thomas. This is just a one-way reference, not a circular reference.
The publisher has a reference to its subscriber(s), so it is able to inform them of the event happening.
A circular reference would be when both have a reference to each other (be it by event subscription or just a variable holding the reference).
Wait... Back up. Clearly your Presenter class has a reference to the form. It was passed into it, as you showed, so that you could do the subscribe. Did you then set the variable holding the form reference to Unknown? Otherwise the reference is still there. But then of course the form class should not go away.
Also, are you sure there is no reference to Presenter from inside itself?
[quote user="Laura Stern"]
Wait... Back up. Clearly your Presenter class has a reference to the form. It was passed into it, as you showed, so that you could do the subscribe. Did you then set the variable holding the form reference to Unknown? Otherwise the reference is still there.
It worked as expected after unhooking the passed in reference.
SIDENOTE: It's not the event subscription that's causing the extra reference. I did my testing for the above without unsubscribing to the events.
[quote user="Laura Stern"]
But then of course the form class should not go away.
This is probably what' caused my confusion. I wasn't expecting the ABL form to be collected because of the passed in reference but it was which lead me to believe that my Presenter should be going out of scope and therefore should be collected too. But if the ABL is using the .NET form's Dispose event to trigger the deallocation the ABL form then I can understand now how the form appears to be being collected.
You were right to be confused because the form should NOT go away if there is a reference to it, even if it has been closed and disposed. I would just verify that the form whose destructor message you're seeing is the one you think it is ... I.e., check the object's ID. But I'm glad the other object is now going away :-)
It looks like in pure (VS2010 based project) .NET environment my original setup is just an object lifetime issue and not a memory leak. Calling GC.Collect() collects the Presenter even with the passed in reference remained unhooked.
It's a memory leak in ABL (Is this expected?). Calling System.GC.Collect() in ABL doesn't collect the Presenter when the passed in reference remained unhooked.
GC:Collect() only directly affects .NET objects. In general, it also will not garbage collect anything that has a reference to it. I.e., it is garbage collection, not delete. When you say the reference "remained unhooked", I assume you mean it has not been cleared and so remains a reference. Though in this situation you actually do have a circular reference: Presenter to the Form and vice versa. So once any references from outside that loop has gone away, .NET will GC the pair of them.
However, GC:Collect() does not have any direct effect on ABL objects. .NET knows nothing about your Presenter class. However, the AVM makes sure that if there is an ABL reference to a .NET instance or a hybrid (ABL class inheriting from .NET, like a form) that that instance has a reference in the .NET space and will not get GC'd. It is actually not a "normal" reference in that we have to explicitly free it. It cannot be detected as part of a circular reference as in pure .NET.
So in this case, the form should not go away as long as presenter holds the reference to it, even if you call GC:Collect.