Hi Levente,

Currently, the right thing to do is to use a WeakIdentityKeyDictionary
and register a finalizer for each key (into the global WeakRegistry).
Something like:

m := Mutex new.
w := WeakIdentityKeyDictionary new.
1 to: 100 do: [ :onDemandInstVars |
        | pseudoPackage |
        pseudoPackage := Object new.
        m critical: [ w at: pseudoPackage put: onDemandInstVars ].
        pseudoPackage
                toFinalizeSend: #cull: "Must be cull: to ignore the argument nil"
                to: [ m critical: [ w finalizeValues ] ]
                with: nil ].
before := w array count: #notNil.
Smalltalk garbageCollectMost.
after := w array count: #notNil.
{ before. after }

Hmm.  For learning or for glory, and because you didn't put quotes around this being the right thing to do, I feel the need to challenge it.  :)

Unless I'm mistaken, this approach would mean finalizeValues will be called for every single element GC'd, which would be immensely wasteful.

Plus, having to register a finalizer for every key?  That seems overly burdensome just to use a WeakKeyDictionary.  It should "just work", but if it can't, the next best thing is to simply calling #finalizeValues manually.

    | w lastClean |
    w := WeakIdentityKeyDictionary new.
    1 to: 100 do: [ :onDemandInstVars |
        | pseudoPackage |
        pseudoPackage := Object new.
        w at: pseudoPackage put: onDemandInstVars ].

Done.  Then, somewhere in the application just put the following line at an appropriate place in the application.  Use the Time guard if necessary."

    (Time millisecondsSince: lastClean) > 30000 ifTrue: 
       [ w finalizeValues. 
       lastClean := Time millisecondClockValue ].

Simple.  Less code, fewer objects, less machine execution including no need for any Mutex serialization.

This is what I'd feel safe enough to call the "right thing to do", in quotes at least..  :)

 - Chris