ImageSegment howto?

goran.hultgren at bluefish.se goran.hultgren at bluefish.se
Tue Apr 16 09:56:41 UTC 2002


I wrote some posts about ImageSegments (that I use at SqueakDot btw:
http://anakin.bluefish.se:8080/squeakdot/) a while ago, I copied them in
below:

Bijan Parsia <bparsia at email.unc.edu> wrote:
> On Mon, 28 Jan 2002 goran.hultgren at bluefish.se wrote:
> > Bijan Parsia <bparsia at email.unc.edu> wrote:
> [snipped violent agreement]
> > > <CSOTD>"OK, I'm finalling doing one of these. This uses ImageSegments to 
> > > help figure out memory consumption of your objects:"
> > > 
> > > String streamContents: [:strm |
> > > 		(ImageSegment new copyFromRootsForExport: Browser allInstances)
> > > 					printSpaceAnalysisOn: strm]
> > > </CSOTD>
> > 
> > Hehe! Go, go...
> 
> Hmm. Now that I've posted this, I can ask: What *exactly* does it
> analyze? How deep does it go?

Ok, an ImageSegment contains all objects that are ONLY reachable from
it's roots.
It uses the GC algorithms in a smart way by essentially withholding the
roots in question
and doing a mark from all other roots (globals). This will leave the
objects in memory
that are only reachable from your segmentroots unmarked.

> Also, if I want to dump and image segment to disk and read it back in
> *not* as a project, how do I do it? Is it worth doing?

Sure! There are basically two ways of using an ImageSegment. One can be
used for "object swapping" like this,
below is a method from an "account" object I have:

swapOut
	"Swaps out this object using an ImageSegment.
	Returns the ImageSegment created. Each reference to me or into
	any of my parts will be turned into an ImageSegmentRootStub
	which acts as an autoloading proxy thus bringing me back in
	at first access. The stubs know the ImageSegment so it does not need
	to be remembered. The ImageSegment can be brought in
	directly by calling #install on it."

	| is myParent |
	myParent _ parent.
	parent _ nil.
	is _ ImageSegment new.
	is copyFromRoots: (Array with: self) sizeHint: 500000 areUnique: true.
	is segmentName: 'account', id fullPrintString.
	is extract; writeToFile.
	parent _ myParent.
	^is

This would "swap out" the object and replace all references with
autoloading stubs. It works like a charm!
And especially loading it back in is FAST.

Here is some code for using ImageSegments for export into possibly other
images that I recently whipped up:

checkpoint
	"Export me using an ImageSegment. Return nil on fail.
	This is used for checkpointing the account on disk
	in a form that can be brought into an independent image.
	We do not overwrite older versions - when the image does a
	snapshot it can purge old checkpoints."

	| is fname myParent dir stream |
	myParent _ parent.
	lastCheckPoint _ TimeStamp current.
	dir _ self dir.
	fname _ dir nextNameFor: self fileName extension: self
fileNameExtension.
	stream _ dir newFileNamed: fname.
	[ parent _ nil.
	is _ ImageSegment new.
	is copyFromRoots: (Array with: self) sizeHint: 1000000 areUnique: true.
	is writeForExportOn: stream ]
		ifError: [parent _ myParent. ^nil].
	parent _ myParent.
	^is

The method above nils out a parent pointer but that is just to "make
sure" - I don't think it is needed. The sizeHint is good to set high
because otherwise there will be
numerous "tries" when dumping large structures and that takes a lot of
time.
The writeForExportOn: method wraps the ImageSegment in a ReferenceStream
in order to gracefully deal with the outpointers.

This needs a new simple method in ImageSegment to work:

writeForExportOn: fileStream
	"Write the segment on the disk with all info needed to reconstruct it
in a new image.  For export.  Out pointers are encoded as normal objects
on the disk."

	| temp |
	state = #activeCopy ifFalse: [self error: 'wrong state'].
	temp _ endMarker.
	endMarker _ nil.
	fileStream fileOutClass: nil andObject: self.
		"remember extra structures.  Note class names."
	endMarker _ temp

And this is how I load them back, a method in my account class (on the
class side):

loadFrom: aDirectory
	"Load the account from given directory."

	| stream account fileName |
	fileName _ aDirectory lastNameFor: self fileName extension: self
fileNameExtension.
	fileName ifNil: [self error: 'Account files not found! Account not
loaded.'].
	stream _ aDirectory oldFileNamed: fileName.
	account _ (stream fileInObjectAndCode) install arrayOfRoots first.
	stream close.
	^account


In order for the above code to work I also needed a method in
FileDirectory:
lastNameFor: baseFileName extension: extension
	"Assumes a file name includes a version number encoded as '.' followed
by digits 
	preceding the file extension.  Increment the version number and answer
the new file name.
	If a version number is not found, set the version to 1 and answer a new
file name"

	| files splits |

	files _ self fileNamesMatching: (baseFileName,'*', self class dot,
extension).
	splits _ files 
			collect: [:file | self splitNameVersionExtensionFor: file]
			thenSelect: [:split | (split at: 1) = baseFileName].
	splits _ splits asSortedCollection: [:a :b | (a at: 2) < (b at: 2)].
	^splits isEmpty 
			ifTrue: [nil]
			ifFalse: [(baseFileName, '.', (splits last at: 2) asString, self
class dot, extension) asFileName]

Well, perhaps it helped!

And... you can see this stuff "in action" at SqueakDot - just sign up,
go to "My SqueakDot", play with "checkpoint" and "account analysis". You
can also see some code by clicking on "source for this page" at the
bottom of most webpages.

regards, Göran



More information about the Squeak-dev mailing list