Squeak
  links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Cleaning up junk
Last updated at 2:53 pm UTC on 16 January 2006
Smalltalk has a garbage collector that ordinarily finds objects that are no longer used and deletes them. However, "unused" items have a sneaky way of being used by something so they won't go away.

This page assumes you are familiar with the basics of garbage collection. It would also be helpful to know about weak references, which are references that do not prevent garbage collection.

Extra Instances

Identifying the problem

First, be sure that all the garbage in your image is cleaned up with
 Smalltalk garbageCollect.
Garbage collection is lazy, so if you don't do this some garbage may be present that the system can take care of.

  MyClass allInstances
or
  MyClass allSubInstances
will turn up all instances of the corresponding types in the image. If there are too many, you need to determine what is holding onto them so that you can cure the problem.

To get a report on all classes at once use
  Smalltalk spaceTally.
This is slow and the report is large; try spaceTallyTo: aFileName. For a more selective approach, try
  Smalltalk spaceForInstancesOf: aClass.

Going to the Root

Any object that is not being garbage collected is referenced from some root object like a GUI or the Smalltalk dictionary. Often if you can find one or two samples you will see the pattern, and know how to do the general cleanup and prevent a recurrence.

  PointerFinder on: anInstance
is the basic tool for finding a root object that holds onto anInstance. It shows the root at the top and then follows the chain down. Note that the chain need not be unique; the tool simply finds one possible route to the root.

Be careful to avoid throwing the tool off with an "easy" path. For example, if you have an inspector up on the instance, PointerFinder will probably report it is the root. So be sure to close all such views and garbage collect. Try to avoid creating intermediates even in the course of opening the PointerFinder.

Since the tool is relatively slow, executing it in a batch over all instances is probably unwise.

If PointerFinder comes up empty, check to be sure you have done a global garbage collect first.

If you want or need a more manual approach, explore "objects pointing to this value" off the main smalltalk operations menu for object inspectors in newer images, or use the following tools:
  Smalltalk browseAllObjectReferencesTo: anObject except: objectsToExclude ifNone: aBlock
  Smalltalk pointersTo:
  Smalltalk pointersTo:except:

Another useful tool is the PointerExplorer: http://map1.squeakfoundation.org/sm/package/1508934b-79ea-4216-956c-1948e9d48aad


Causes of Extra Instances

It is rather easy for an operation to make copies of objects as a side effect, and this may cause you to have many instances where you think you have only one.

Objects can hide in the Smalltalk dictionary or in class variables. If dependency notifications are not cleaned up properly, objects can hide there. The Object class variables EventsFields and DependentsField are general holders for dependency information for the new event based (#when:send:to:) and older update/change mechanism respectively. Note that dependency information may go in instance variables if you subclass EventModel (new scheme) or Model (old scheme).

  Object>>release
provides the general way one should practice good housekeeping to avoid dependents building up. However, it is neither necessary nor sufficient. It is unnecessary in that the dependency mechanism uses weak references, so when objects disappear objects that send notifications to them should (eventually) lose their references without any explicit action. It is insufficient for two reasons. First, you must make special arrangements to have release called; it does not automatically get called when an object gets garbage collected. Second, if you add some arguments to the event dependency mechanism you can defeat the operation of the weak references. The next paragraph shows you how.

Here's one cautionary tale: I had a list aList that contained a bunch of links. The links point to data. So I said
  aList do: [:aLink | aLink data when: #changedStart send: #updateStart: to: aList with: aLink].
When the data changes the list doesn't have to look for the data because it gets the link as an argument. Since one data object may be in several lists, it doesn't know where it is on its own, so I added that. Wonderful. The problem is that aLink is held strongly in the event notification mechanism, and it references its data, which is the sender. So even if you delete the list, the event notification will not go away. Neither will the data, even if all lists referencing it are deleted. So if you install event notification on aNotifier, don't put aNotifier in the argument list (even indirectly). aNotifier can, of course, send itself dynamically as an argument with the notification . So the notifier's code could say
  self trigger: #changedStart with: self.

If you make a script, even if you delete the script the objects being scripted may get attached to the generated class aka Uniclass (with names like PlayerNN).

I believe (but have not confirmed) that Processes will hold onto their objects, because Processes themselves are rooted. The process inspector tool allows you to find what is holding onto a process. So you may need to terminate the process to release the objects involved.


Obsolete Classes


Under certain conditions (that I no longer remember distinctly; I think they involve file ins) you may change the definition of a class and have the old definition hang around (to support existing instances). The class is kept in the Smalltalk dictionary with a name like ObsoleteMyClass.
  Smalltalk obsoleteClasses

(NOTE: at least since Squeak 3.9alpha SystemDictionary>>obsoleteClasses has been deprecated. Use SystemNavigation default obsoleteClasses)

gives you a way to find them. Probably this also happens if you delete a class but it has live instances. You need to track down the instances (use the #allInstances method), clean them up, and get rid of the class.

Another source of obsolete classes is specific to squeak and the new scripting system. Scripts create classes behind the scenes to implement their behavior. These are subclasses of Player and have names like Player29. Even if you delete the script (that is the morph) the classes will remain. As mentioned above, the classes can root instance references. They also clutter things up; you can delete them in the system browser if you don't need them.

The following methods may be helpful in cleaning things up, though they didn't do much for me:
  Player class>>freeUnreferencedSubclasses
  Player class>>abandonUnnecessaryUniclasses

Squeak 3.2 has a lot of obsolete classes. Smalltalk inspect reveals that the SystemDictionary contains (for some unknown reason) not only classes that are keyed by Symbols, but also String-valued keys. All these string-valued keys can be removed with these statements:

  | collection |
  collection := OrderedCollection new.
  Smalltalk associationsDo:
    [:assoc | (assoc key class == Symbol)
               ifFalse: [collection add: assoc key]].
  collection do:
      [:k | Smalltalk removeKey: k].


In my experience, this removes most - if not all obsolete classes. For complete removal of obsolete classes it is seemingly necessary to zap all project histories (?).

My (Herbert König) experience temporary variables:


After refactoring I inadvertedly deleted a class and filed it in again from a previous fileout.
After that a method like
  myMethod
     | tempVar |
      tempVar := DeletedAndRestoredClass new.
      tempVar validMessageToTempVar.

created an exception "AnObsoleteDeletedAndRestoredClass does not understand validMessageToTempVar".

Tried in vain everything presented here and every hint Dan Ingalls kindly provided.

Two solutions
1- filed out every category I wrote and filed it in in a fresh image.
2- Changed the metod to:
  myMethod
     | tempVar |
      tempVar := nil.
      tempVar := DeletedAndRestoredClass new.
      tempVar validMessageToTempVar.

sent the method once, deleted the change.

Both worked and after finding out what happened I'll update it here.

pointers welcome to herbertkoenig@gmx.net

Monticello

In 3.8gamma Monticello consumes a lot of space. The object can be released with:
MCFileBasedRepository flushAllCaches.
Smalltalk garbageCollect.

Other


See the housekeeping protocol of SystemDictionary. Reference these like this:
  Smalltalk obsoleteBehaviors
  Smalltalk obsoleteMethodReferences

Anyone who can provide details about how such things get created, please fill them in here.

You may also find FAQ: Deleting a Morph useful.

This page created 2 January, 2002 by Ross Boylan, based on Squeak 3.2 alpha. I'd like to thank the squeak list, and particularly Bob Arning, for their help with these matters. Any errors are my own.

You can reach me at RossBoylan@stanfordalumni.org.


Use the following at your own risk!

This nil's all remaining subclasses. Be very cautious with this, because the errors caused by this could appear later.

Example use:
Smalltalk discardSoundAndSpeech
leaves obsolete classes and instances, round about 70. It's very boring to hunt them using the PointerFinder. This way, you get rid of these very quickly.
<pre>
Smalltalk obsoleteClassesDo: [ :c |
  c allInstancesDo: [ :i |
    i becomeForward: nil.
  ].
  c becomeForward: nil.
].
</pre>


The PointerFinder has a bug that prevents it from finding references from CompiledMethods to objects. You can find these with the PointerFinder after applying the following changeset.
PointerFinder-fix-RAA.cs