>>finalize questions

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

>>finalize questions

Michael van der Gulik
Hi all.

Two questions for today.

---------
1. What does Object>>finalize do?

It appears not to do much at all. I had a quick poke at the VM source
(but not enough to grok what's happening). It appears to only be used
for weak references or something?

I made a test class (Dunce) and implemented:

finalize
     Transcript show: 'finalize called..'.

but the Transcript remains blank even lots of "Dunce new" and then
"Dunce instanceCount" (returns 1).

Also, "w := WeakArray with: (Dunce new)" doesn't call finalize either...

-----------
2. I have an object with a process "in" it. How can I make sure that
process is terminated when there are no more references to that object?

I assume processes are automagically added to the garbage collector's
root pointer collection, preventing that object from being garbage
collected.

i.e.:

Dunce>>aProcess
        [ [ self doSomeCleanUpAndWait5Seconds ] repeat ]
                forkNamed: 'Silly process'.

Michael vdG.


Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

Bert Freudenberg-3
Am 20.08.2006 um 07:48 schrieb Michael van der Gulik:
> 1. What does Object>>finalize do?
>
> It appears not to do much at all.

Right. It's simply the default implementation when a WeakRegistry  
sends #finalize to one of its registered objects, to prevent a  
MessageNotUnderstood walkback.

> Also, "w := WeakArray with: (Dunce new)" doesn't call finalize  
> either...

WeakArrays are not to support finalization but to hold weakly onto  
its elements (replaced by nil when GCed).

For finalization use a WeakRegistry:

        WeakRegistry default add: Dunce new.
        Smalltalk garbageCollect.

> 2. I have an object with a process "in" it. How can I make sure  
> that process is terminated when there are no more references to  
> that object?

Terminate the process when you release the last reference to the  
object. Like, send #release to your object before setting the ref to  
nil.

Only as a last resort, use finalization.

An object cannot finalize itself, because that would create a strong  
reference! Instead, #finalize is sent to an object's #executor. By  
default, the executor is a clone of the object (held strongly) and  
#finalize is sent to that clone.

Ponder that.

It means that any change to instance variables after registering for  
finalization does not affect the executor. So you must first create  
the process, then register for finalization. When the process inst  
variable changes, you need to register a finalizer again.

You can verify this by using "Dunce allInstances". For debugging  
purposes, it's helpful to modify #printOn: to indicate whether this  
instance is an executor or not. I usually set some inst var to nil in  
#actAsExecutor, because the executor only needs access to the inst  
vars it must finalize.

Using this technique, you can verify that the executor survives the  
GC that swiped the original object. Only the next GC will collect the  
executor, too.

In short - use finalization only if you really, really need it and  
understand the implications ;-)

- Bert -


Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

Michael van der Gulik
Thanks, Bert.

Bert Freudenberg wrote:
>> 2. I have an object with a process "in" it. How can I make sure  that
>> process is terminated when there are no more references to  that object?
>
>
> Terminate the process when you release the last reference to the  
> object. Like, send #release to your object before setting the ref to  nil.
>
> Only as a last resort, use finalization.

I thought of a better option today: Create a "PollingService" singleton.
  The process I needed was just meant to slowly poll something.

Rather than doing "[[self doSomething] repeat ] fork", I could ask the
PollingService to poll me on a regular basis. The polling service would
have something like:

PollingService>>pollObject: n
        | o |
        o := myWeakArray at: n.
        [ o isNil ] whileFalse: [ o poll. o:= myWeakArray at: n. ].

So that when the object is garbage collected, the thread would also stop
iterating. A very useful pattern!

This could maybe even be done on the class side of that object.

I hope the small amount of time between loop iterations is enough to get
the GC to make the weak reference nil.

Michael.


Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

Bert Freudenberg-3
Michael van der Gulik schrieb:

> Thanks, Bert.
>
> Bert Freudenberg wrote:
>>> 2. I have an object with a process "in" it. How can I make sure  that
>>> process is terminated when there are no more references to  that object?
>>
>>
>> Terminate the process when you release the last reference to the  
>> object. Like, send #release to your object before setting the ref to  
>> nil.
>>
>> Only as a last resort, use finalization.
>
> I thought of a better option today: Create a "PollingService" singleton.
>  The process I needed was just meant to slowly poll something.
>
> Rather than doing "[[self doSomething] repeat ] fork", I could ask the
> PollingService to poll me on a regular basis. The polling service would
> have something like:
>
> PollingService>>pollObject: n
>     | o |
>     o := myWeakArray at: n.
>     [ o isNil ] whileFalse: [ o poll. o:= myWeakArray at: n. ].
>
> So that when the object is garbage collected, the thread would also stop
> iterating. A very useful pattern!
>
> This could maybe even be done on the class side of that object.
>
> I hope the small amount of time between loop iterations is enough to get
> the GC to make the weak reference nil.

Unlikely. Once an object gets tenured, only a full GC will collect it:

        http://minnow.cc.gatech.edu/squeak/1469

Do you really have random objects that are not owned by some specific
object that could release them?

- Bert -

Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

Michael van der Gulik
Bert Freudenberg wrote:

> Michael van der Gulik schrieb:
>> I thought of a better option today: Create a "PollingService"
>> singleton.  The process I needed was just meant to slowly poll something.
>>
>> Rather than doing "[[self doSomething] repeat ] fork", I could ask the
>> PollingService to poll me on a regular basis. The polling service
>> would have something like:
>>
>> PollingService>>pollObject: n
>>     | o |
>>     o := myWeakArray at: n.
>>     [ o isNil ] whileFalse: [ o poll. o:= myWeakArray at: n. ].
>>
>> So that when the object is garbage collected, the thread would also
>> stop iterating. A very useful pattern!
>>
>> This could maybe even be done on the class side of that object.
>>
>> I hope the small amount of time between loop iterations is enough to
>> get the GC to make the weak reference nil.
>
>
> Unlikely. Once an object gets tenured, only a full GC will collect it:
>
>     http://minnow.cc.gatech.edu/squeak/1469
Ahh... I'd like to confirm that my approach indeed doesn't work, even if
I stick a 'Smalltalk garbageCollect' in the middle of it. Argh.

Code attached, if you can be bothered looking at it.

> Do you really have random objects that are not owned by some specific
> object that could release them?

Well, probably. I've just had another idea that I'll try.

Michael.

'From Squeak3.8 of ''5 May 2005'' [latest update: #6665] on 22 August 2006 at 10:07:34 pm'!
Object subclass: #TestPoller
        instanceVariableNames: 'delay'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Playing'!
!TestPoller commentStamp: 'mvdg 8/22/2006 22:07' prior: 0!
Test a polling pattern I made. It doesn't work.

p := TestPoller new.
p := nil.

Now, the transcript should stop showing 'bleep'. If your image is important, you'll need to stop the Bleeper process.!


!TestPoller methodsFor: 'as yet unclassified' stamp: 'mvdg 8/22/2006 21:43'!
initialize
        delay := Delay forSeconds: 1.
        self class startPolling: self.! !

!TestPoller methodsFor: 'as yet unclassified' stamp: 'mvdg 8/22/2006 21:42'!
poll
        Transcript show: 'bleep '.
        delay wait.! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

TestPoller class
        instanceVariableNames: ''!

!TestPoller class methodsFor: 'as yet unclassified' stamp: 'mvdg 8/22/2006 22:04'!
startPolling: object
        | w x |
        w := WeakArray with: object.
        x := w at: 1.
        [ [x isNil] whileFalse: [
                x poll.
                " Try very hard to get x garbage collected:"
                x := nil.
                Smalltalk garbageCollect.
                Processor yield.
                x := w at: 1.
        ] ] forkNamed: 'Bleeper'.! !


Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

Bert Freudenberg-3
Michael van der Gulik schrieb:
> Bert Freudenberg wrote:
>> Michael van der Gulik schrieb:
> I'd like to confirm that my approach indeed doesn't work, even if
> I stick a 'Smalltalk garbageCollect' in the middle of it. Argh.

You should try to trace the non-GCed instance of TestPoller. My guess
would be that it is still referenced by the #startPolling: method
context which is referenced by the process that executes the block you
forked.

- Bert -

Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

johnmci
In reply to this post by Michael van der Gulik

On 22-Aug-06, at 3:10 AM, Michael van der Gulik wrote:

>>>
>>> I hope the small amount of time between loop iterations is enough  
>>> to get the GC to make the weak reference nil.
>> Unlikely. Once an object gets tenured, only a full GC will collect  
>> it:
>>     http://minnow.cc.gatech.edu/squeak/1469
>
> Ahh... I'd like to confirm that my approach indeed doesn't work,  
> even if I stick a 'Smalltalk garbageCollect' in the middle of it.  
> Argh.

Let me note that over the years I've had to fix a number of  
application for clients who were *hosed* because they were
abusing finalization because.

a) Finalization will happen, but later (when, is a good question?  
Milliseconds, to months...).

b) Finalization might not occur because of some issue with how the VM  
does the finalization,
perhaps a queue overflows, or a process crashes, or you hit a  
boundary condition, etc.

c) Finalization might not happen because someone resurrects the object.

Unless you accept these three points, then basing application level  
infrastructure on finalization is fraught with issues.




========================================================================
===
John M. McIntosh <[hidden email]>
Corporate Smalltalk Consulting Ltd.  http://www.smalltalkconsulting.com
========================================================================
===



Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

Michael van der Gulik
In reply to this post by Bert Freudenberg-3
Bert Freudenberg wrote:

> Michael van der Gulik schrieb:
>
>> Bert Freudenberg wrote:
>>
>>> Michael van der Gulik schrieb:
>>
>> I'd like to confirm that my approach indeed doesn't work, even if
>> I stick a 'Smalltalk garbageCollect' in the middle of it. Argh.
>
>
> You should try to trace the non-GCed instance of TestPoller. My guess
> would be that it is still referenced by the #startPolling: method
> context which is referenced by the process that executes the block you
> forked.
You are right. In fact, the strong reference was sitting right there in
front of me as a method parameter (/me slaps forehead).

For completeness, a working version of the code is attached. However,
I've refactored the need for it away, so it's essentially useless now :-}.

Michael.

'From Squeak3.8 of ''5 May 2005'' [latest update: #6665] on 26 August 2006 at 5:19:53 pm'!
Object subclass: #TestPoller
        instanceVariableNames: 'delay'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Playing'!
!TestPoller commentStamp: 'mvdg 8/22/2006 22:07' prior: 0!
Test a polling pattern I made. It doesn't work.

p := TestPoller new.
p := nil.

Now, the transcript should stop showing 'bleep'. If your image is important, you'll need to stop the Bleeper process.!


!TestPoller methodsFor: 'as yet unclassified' stamp: 'mvdg 8/22/2006 21:43'!
initialize
        delay := Delay forSeconds: 1.
        self class startPolling: self.! !

!TestPoller methodsFor: 'as yet unclassified' stamp: 'mvdg 8/22/2006 21:42'!
poll
        Transcript show: 'bleep '.
        delay wait.! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

TestPoller class
        instanceVariableNames: ''!

!TestPoller class methodsFor: 'as yet unclassified' stamp: 'mvdg 8/24/2006 22:46'!
startPollingOnWeakArray: w
        | keepPolling |
        keepPolling := true.
        [ [keepPolling] whileTrue: [
                " Try very hard to get x garbage collected:"
                ((w at: 1) isNil) ifTrue: [keepPolling := false] ifFalse: [ (w at: 1) poll ].
                Smalltalk garbageCollectMost.
        ] ] forkNamed: 'Bleeper'.
! !

!TestPoller class methodsFor: 'as yet unclassified' stamp: 'mvdg 8/24/2006 22:31'!
startPolling: obj
        self startPollingOnWeakArray: (WeakArray with: obj).
        " Need to call another method because otherwise we'd be holding an immutable reference to obj. "! !


Reply | Threaded
Open this post in threaded view
|

Re: >>finalize questions

Bert Freudenberg-3

Am 27.08.2006 um 06:50 schrieb Michael van der Gulik:

> Bert Freudenberg wrote:
>> Michael van der Gulik schrieb:
>>> Bert Freudenberg wrote:
>>>
>>>> Michael van der Gulik schrieb:
>>>
>>> I'd like to confirm that my approach indeed doesn't work, even if
>>> I stick a 'Smalltalk garbageCollect' in the middle of it. Argh.
>> You should try to trace the non-GCed instance of TestPoller. My  
>> guess would be that it is still referenced by the #startPolling:  
>> method context which is referenced by the process that executes  
>> the block you forked.
>
> You are right. In fact, the strong reference was sitting right  
> there in front of me as a method parameter (/me slaps forehead).
>
> For completeness, a working version of the code is attached.

Well, this line

        ((w at: 1) isNil) ifTrue: [keepPolling := false] ifFalse: [ (w at:  
1) poll ].

is incorrect - a GC might well happen between the #isNil check and  
the #poll send, so the weak ref might have become nil by the time you  
send #poll.

- Bert -