How to explore objects pinned in memory
This page contains scripts and strategies useful for debugging pinned in memory objects.
Overview
Pinned in memory objects are normal during the execution. However, if they cannot be garbaged collected, they will lead to an increase in memory size, as they cannot be moved around when compacting memory.
Common issues
Parameters to FFI calls are pinned in memory. If one of those parameters is a string literal, it will not be garbaged collected.
Useful Scripts
Count all objects that are pinned in memory
2 timesRepeat: [ Smalltalk garbageCollect ]. (SystemNavigation default allObjects select: [ :e | e isPinnedInMemory ]) size
Print string for all objects that are pinned in memory.
Here we just get the string representation as getting the actual objects can create references to it, making it harder to find from where they are referenced
2 timesRepeat: [ Smalltalk garbageCollect ]. (SystemNavigation default allObjects select: [ :e | e isPinnedInMemory ]) collect: [ :each | each printString ]
Select all objects that are pinned in memory.
SystemNavigation default allObjects select: [ :e | e isPinnedInMemory ]
To get accurate results it is best to do a few gargage collects before, so pinned objects that can be gargaged collected are removed.
2 timesRepeat: [ Smalltalk garbageCollect ]. (SystemNavigation default allObjects select: [ :e | e isPinnedInMemory ])
Unpinning all pinned objects
(SystemNavigation default allObjects select: [ :e | e isPinnedInMemory ]) do: [ :e | e unpinInMemory ]
Exploring pinned objects
2 timesRepeat: [ Smalltalk garbageCollect ]. pinned := SystemNavigation default allObjects select: [ :each | each isPinnedInMemory ]
pinned size
pinned groupedBy: #className
Exploring pinned objects by avoiding any new references
2 timesRepeat: [ Smalltalk garbageCollect ]. pinnedData := (SystemNavigation default allObjects select: [ :each | each isPinnedInMemory]) collect: [ :each | each class name -> each printString ].
pinnedData groupedBy: #key
References to pinned objects
We can use ReferenceFinder
to find references to a particular pinned objects.
To get accurate results it is best to do a few garbage collects before, and not store the list of pinned objects in any local variable or inspect it
NOTE: This does not work if the pinned objects is a method literal. So of the list of references to a pinned objects that is not garbage collected is nil, it means that the object is most likely a methid literal.
2 timesRepeat: [ Smalltalk garbageCollect ]. ReferenceFinder findPathTo: ((SystemNavigation default allObjects select: [ :e | e isPinnedInMemory ]) at: 4)
Possible issues
Handle MessageCatcher
MessageCatcher
instances can be problematic when iterating over all objects to find pinned objects. That is because Object>>#isPinnedInMemory
no longer returns a boolean when used within a select block.
The code below adds isPinnedInMemory to MessageCatcher
MessageCatcher compile: (Object>>#isPinnedInMemory) sourceCode classified: (Object>>#isPinnedInMemory) category
Memory size
It can also be useful to compute the memory size to understand the effects of pinned objects
SystemNavigation default allObjects sum: #sizeInMemory
Smalltalk vm statisticsReport