Cleaning up junk
Last updated at 5:05 pm UTC on 14 January 2019
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.
Below are notes with suggestions how to free additional objects if an image has become large and space is needed in order for example to do an export of data.
Notes from 2016
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.
Identifying the problem
First, be sure that all the garbage in your image is cleaned up with
Garbage collection is lazy, so if you don't do this some garbage may be present that the system can take care of.
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
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
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).
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.
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.
(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:
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.
[:assoc | (assoc key class == Symbol)
ifFalse: [collection add: assoc key]].
[: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
| tempVar |
tempVar := DeletedAndRestoredClass new.
created an exception "AnObsoleteDeletedAndRestoredClass does not understand validMessageToTempVar".
Tried in vain everything presented here and every hint Dan Ingalls kindly provided.
1- filed out every category I wrote and filed it in in a fresh image.
2- Changed the metod to:
| tempVar |
tempVar := nil.
tempVar := DeletedAndRestoredClass new.
sent the method once, deleted the change.
Both worked and after finding out what happened I'll update it here.
pointers welcome to email@example.com
In 3.8gamma Monticello consumes a lot of space. The object can be released with:
See the housekeeping protocol of SystemDictionary. Reference these like this:
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.
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.
Smalltalk obsoleteClassesDo: [ :c |
c allInstancesDo: [ :i |
i becomeForward: nil.
c becomeForward: nil.
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.