OO Pub/Sub & Garbage Collection

Posted by Tim Kuehn on 29-Dec-2014 15:55

W7 64 bit

OE PDS 11.5 64 bit
I'm working on an OO PUB / SUB example that'll demonstrate that a set of objects in a pub/sub relationship will not be garbage collected when the program that contains the various objects  goes out of scope. 
The problem is, I'm not seeing any objects still around after the containing procedure goes out of scope. 
So, my question is if this problem has been fixed, and if so, in what version did this happen? 

All Replies

Posted by Mike Fechner on 29-Dec-2014 16:52

The attached demos the issue on 11.4.

test.p creates an instance of ClassA

ClassA in the constructor creates an instance of ClassB and subscribes to the event. This causes the circular reference between the instance of ClassA and ClassB - as the instance of B keeps a reference of the instance of A to be able to execute the event handler as a callback.

test.p then assigns it’s reference to the instance of ClassA to ?. And the objects don’t go out of scope (messages from destructors only shown when the session exits).

When you remove the event subscription from ClassA’s constructor, both objects will go out of scope when test.p assigns the reference variable to ?.

Similar behavior between ClassC and ClassD – where C subscribes to an event from D and D subscribes to an event from C. No other references between C and D are kept besides the event subscription.

Run test.p to see that after test.p clears the reference both objects still stay around until after test.p exists (and the session closes).

 

Posted by Mike Fechner on 29-Dec-2014 17:15

And now with the attachments.

Posted by Rom Elwell on 29-Dec-2014 17:24

Great stuff, Mike.  Thank you sir.

Posted by Tim Kuehn on 29-Dec-2014 18:32

Looking at the code, I can see how the structure's a problem.

Since both "A" and "C" hold references to "B" and "D", then B and D will never go out of scope and get collected.

If the event subscription constitutes another reference, then that results in both scenarios having a circular reference, the classes will never go out of scope, and the Julian memory leak will strike again. 

Personally I'd structure it differently:

  1. Create one or more classes that are needed to generate the events.
  2. Create the appropriate classes to receive the events.
  3. Create a container class to instantiate the classes created in steps 1 & 2, have them match up Publishes with Subscriptions as needed, fire off whatever process was required to do the work, and then report the results back to it's parent when the work was done. 

This would completely avoid the circular references you're seeing now, the "Event" classes would get collected when the container class was collected, and then the "worker" classes when then go away. 

Posted by Mike Fechner on 30-Dec-2014 00:25

Don't blame the code... You were asking for a sample demoing that an event subscription counts as a reference.

I would not see this as a memory leak or AVM bug. For me this is expected behavior. Even when not 100% obvious at the beginning.

Von meinem Windows Phone gesendet

Von: Tim Kuehn
Gesendet: ‎30.‎12.‎2014 01:32
An: TU.OE.General@community.progress.com
Betreff: RE: [Technical Users - OE General] OO Pub/Sub & Garbage Collection

Reply by Tim Kuehn

Looking at the code, I can see how the structure's a problem.

Since both "A" and "C" hold references to "B" and "D", then B and D will never go out of scope and get collected.

If the event subscription constitutes another reference, then that results in both scenarios having a circular reference, the classes will never go out of scope, and the Julian memory leak will strike again. 

Personally I'd structure it differently:

  1. Create one or more classes that are needed to generate the events.
  2. Create the appropriate classes to receive the events.
  3. Create a container class to instantiate the classes created in steps 1 & 2, have them match up Publishes with Subscriptions as needed, fire off whatever process was required to do the work, and then report the results back to it's parent when the work was done. 

This would completely avoid the circular references you're seeing now, the "Event" classes would get collected when the container class was collected, and then the "worker" classes when then go away. 

Stop receiving emails on this subject.

Flag this post as spam/abuse.

Posted by Mike Fechner on 30-Dec-2014 00:26

And by the way, C does not hold a reference to D - other than the event subscription.

Von meinem Windows Phone gesendet

Von: Tim Kuehn
Gesendet: ‎30.‎12.‎2014 01:32
An: TU.OE.General@community.progress.com
Betreff: RE: [Technical Users - OE General] OO Pub/Sub & Garbage Collection

Reply by Tim Kuehn

Looking at the code, I can see how the structure's a problem.

Since both "A" and "C" hold references to "B" and "D", then B and D will never go out of scope and get collected.

If the event subscription constitutes another reference, then that results in both scenarios having a circular reference, the classes will never go out of scope, and the Julian memory leak will strike again. 

Personally I'd structure it differently:

  1. Create one or more classes that are needed to generate the events.
  2. Create the appropriate classes to receive the events.
  3. Create a container class to instantiate the classes created in steps 1 & 2, have them match up Publishes with Subscriptions as needed, fire off whatever process was required to do the work, and then report the results back to it's parent when the work was done. 

This would completely avoid the circular references you're seeing now, the "Event" classes would get collected when the container class was collected, and then the "worker" classes when then go away. 

Stop receiving emails on this subject.

Flag this post as spam/abuse.

Posted by Tim Kuehn on 12-Jan-2015 11:38

It turns out this is a common OO problem - there's even a wiki page on it!

en.wikipedia.org/.../Lapsed_listener_problem

Posted by Lieven De Foor on 14-Jan-2015 04:22

In .NET and Java there's a way to use weak references, which don't prevent garbage collection of the object they're referring to. If these could be implemented in ABL, than that could be a solution to this problem...

Posted by Lieven De Foor on 14-Jan-2015 04:28

You could try experimenting with the .NET WeakReference class, but I doubt it will work as expected inside the AVM runtime..

Posted by Peter Judge on 14-Jan-2015 11:27

I've used weak references in the past via the INTEGER() function, which returns a reference ("handle") to the object. You can store this anywhere as an integer without the object being prevented from garbage collection.

Turning an integer value into an object again requires that you walk the session object tree. From around 11.4 there's a method called  ResolveWeakReference in OpenEdge.Core.Session which can do this for you, although it's trivial to implement (method included here)

/** Resolves a weak reference into an object instance. A weak reference is an integer
        representation of an object reference. This method is analogous to the WIDGET-HANDLE()
        function.
        
        Notes: * Based on http://msdn.microsoft.com/en-us/library/ms404247(v=VS.90).aspx
               * Performance of ResolveWeakReference() will probably suck.
               * An ABL statement "OBJECT-REFERENCE(int)" would entirely replace this method.    
        @param integer A weak reference to an object.
        @return Object The object instance corresponding to that reference. The unknown value/null
                is returned if the referecen cannot be resolved.  */
    method static public Object ResolveWeakReference(input piReference as integer):
        define variable oInstance as Object no-undo.
        define variable oReference as Object no-undo.
        
        oInstance = session:first-object.
        do while valid-object(oInstance) and not valid-object(oReference):
            if piReference eq int(oInstance) then
                oReference = oInstance.
            oInstance = oInstance:Next-Sibling.
        end.
        
        return oReference.        
    end method.


This approach is not as sophisticated as the WeakReference class, but seems to work well enough.

-- peter

Posted by Lieven De Foor on 16-Jan-2015 04:40

Great find Peter! I'm just a bit worried about the "Performance of ResolveWeakReference() will probably suck."  remark...

Have you done any performance tests of this method on applications with a reasonably large amount of objects?

Posted by Peter Judge on 16-Jan-2015 07:49

> Have you done any performance tests of this method on applications with a reasonably large amount of objects?

'suck' is relative.  It's probably more accurate to say that the more objects you have in the session, the slower this search will be:  since it'll be done in O(n) time.
 
It'll also suck as compared to a built-in function.  
 
I did some tests a while ago*, and iterating through the object tree with 100k objects in it took ~325ms. That's not especially fast, but I've no good feel for whether that's a lot of objects (hence the earlier thread).
 
-- peter
 
 
 
[collapse]
From: Lieven De Foor [mailto:bounce-lievendefoormipsbe@community.progress.com]
Sent: Friday, 16 January, 2015 05:41
To: TU.OE.General@community.progress.com
Subject: RE: [Technical Users - OE General] OO Pub/Sub & Garbage Collection
 
Reply by Lieven De Foor

Great find Peter! I'm just a bit worried about the "Performance of ResolveWeakReference() will probably suck."  remark...

Have you done any performance tests of this method on applications with a reasonably large amount of objects?

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

This thread is closed