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. |
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 - |
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. |
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 - |
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 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'.! ! |
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 - |
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 ======================================================================== === |
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. 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. "! ! |
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 - |
Free forum by Nabble | Edit this page |