Gofer new squeaksource: 'UsedObjects'; package: 'ConfigurationOfUsedObjects'; load.
(Smalltalk at: #ConfigurationOfUsedObjects) project lastVersion load.
UnusedObjectDiscoverer current startDiscovery. Transcript show: 'Do something'. Transcript browse. UnusedObjectDiscoverer current stopDiscoveryAndGetStadistics inspect.Or:
UnusedObjectDiscoverer analyse: [Transcript show: 'Do something'.]
Total amount of objects: 955887 Amount of used objects: 3804 Percentage of used objects: 0.397954988403441 Amount of unused objects: 952083 Percentage of unused objects: 99.60204501159656
Total bytes of memory used: 44408428 Bytes of memory for the used objects: 6617252 Percentage of memory for the used objects: 14.90089223604132 Bytes of memory for the unused objects: 37791176 Percentage of memory for the unused objects: 85.09910776395868
It is important to understand which parts of the system are instantiated but also which are used or unused. We define that a used object is one that receives a message or that is directly used by the VirtualMachine, during a specific period of time.
The main challenge here is where to store the mark of each object. Adding an instance variable to Object (the root class of the Smalltalk hierarchy chain) and store there a Boolean object is not possible in Pharo, and the requirement of an extra reference for each object is highly memory consuming. Our solution modifies the Pharo VM so that we can use an empty bit of the object header to mark objects as used. That way we do not use extra memory and it works efficiently. We also modify the code of the VM that implements the message send to turn on the bit when an object receives a message.
Take a Pharo image an evaluate:
Gofer new squeaksource: 'UsedObjects'; package: 'ConfigurationOfUsedObjects'; load.
(Smalltalk at: #ConfigurationOfUsedObjects) project lastVersion load.
The main class is called UnusedObjectDiscoverer and lets you find used objects during an execution. In order to do that:
1) You first have to evauate: UnusedObjectDiscoverer current startDiscovery. With that, you mark all objects as unused so that you can start a clean discovery.
2) Then, you do things. Run a seaside app, develope, code, etc.
3) Once you are finished and want to see the usage, print or inspect: UnusedObjectDiscoverer current stopDiscoveryAndGetStadistics This will get the statistics and unmark all objects again.
Another option is to do: UnusedObjectDiscoverer analyse: aBlock where aBlock is what you want to analyze.
Example:
UnusedObjectDiscoverer current startDiscovery. Transcript show: 'Do something'. Transcript browse. UnusedObjectDiscoverer current stopDiscoveryAndGetStadistics inspect.
Or:
UnusedObjectDiscoverer analyse: [Transcript show: 'Do something'.]
Once you finished getting statistics you want to look at them. The first naive way is to just print the result of #stopDiscoveryAndGetStadistics which is a UnusedObjectStadistics instance. So for example if you print such object you get something like this:
Total amount of objects: 955887 Amount of used objects: 3804 Percentage of used objects: 0.397954988403441 Amount of unused objects: 952083 Percentage of unused objects: 99.60204501159656
Total bytes of memory used: 44408428 Bytes of memory for the used objects: 6617252 Percentage of memory for the used objects: 14.90089223604132 Bytes of memory for the unused objects: 37791176 Percentage of memory for the unused objects: 85.09910776395868
Of course, just analyzing the numbers of the statistics may not be enough in certain scenarios. For such purpose, we have developed special visualizations.